@wix/headless-stores 0.0.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.
- package/astro/actions/package.json +4 -0
- package/cjs/dist/astro/actions/custom-checkout.d.ts +50 -0
- package/cjs/dist/astro/actions/custom-checkout.js +53 -0
- package/cjs/dist/astro/actions/index.d.ts +1 -0
- package/cjs/dist/astro/actions/index.js +1 -0
- package/cjs/dist/data-component-tags.d.ts +8 -0
- package/cjs/dist/data-component-tags.js +9 -0
- package/cjs/dist/enums/index.d.ts +2 -0
- package/cjs/dist/enums/index.js +2 -0
- package/cjs/dist/enums/social-platform-enums.d.ts +25 -0
- package/cjs/dist/enums/social-platform-enums.js +27 -0
- package/cjs/dist/enums/sort-enums.d.ts +17 -0
- package/cjs/dist/enums/sort-enums.js +18 -0
- package/cjs/dist/react/Category.d.ts +242 -0
- package/cjs/dist/react/Category.js +235 -0
- package/cjs/dist/react/CategoryList.d.ts +107 -0
- package/cjs/dist/react/CategoryList.js +91 -0
- package/cjs/dist/react/Choice.d.ts +211 -0
- package/cjs/dist/react/Choice.js +213 -0
- package/cjs/dist/react/Option.d.ts +242 -0
- package/cjs/dist/react/Option.js +346 -0
- package/cjs/dist/react/Product.d.ts +1065 -0
- package/cjs/dist/react/Product.js +1157 -0
- package/cjs/dist/react/ProductList.d.ts +400 -0
- package/cjs/dist/react/ProductList.js +368 -0
- package/cjs/dist/react/core/CategoryList.d.ts +194 -0
- package/cjs/dist/react/core/CategoryList.js +180 -0
- package/cjs/dist/react/core/Product.d.ts +225 -0
- package/cjs/dist/react/core/Product.js +190 -0
- package/cjs/dist/react/core/ProductList.d.ts +235 -0
- package/cjs/dist/react/core/ProductList.js +217 -0
- package/cjs/dist/react/core/ProductListFilters.d.ts +138 -0
- package/cjs/dist/react/core/ProductListFilters.js +242 -0
- package/cjs/dist/react/core/ProductListPagination.d.ts +49 -0
- package/cjs/dist/react/core/ProductListPagination.js +41 -0
- package/cjs/dist/react/core/ProductListSort.d.ts +19 -0
- package/cjs/dist/react/core/ProductListSort.js +52 -0
- package/cjs/dist/react/core/ProductModifiers.d.ts +416 -0
- package/cjs/dist/react/core/ProductModifiers.js +413 -0
- package/cjs/dist/react/core/ProductVariantSelector.d.ts +313 -0
- package/cjs/dist/react/core/ProductVariantSelector.js +291 -0
- package/cjs/dist/react/core/SelectedVariant.d.ts +230 -0
- package/cjs/dist/react/core/SelectedVariant.js +269 -0
- package/cjs/dist/react/index.d.ts +6 -0
- package/cjs/dist/react/index.js +6 -0
- package/cjs/dist/react/types.d.ts +8 -0
- package/cjs/dist/react/types.js +9 -0
- package/cjs/dist/server-actions/custom-checkout-action.d.ts +49 -0
- package/cjs/dist/server-actions/custom-checkout-action.js +64 -0
- package/cjs/dist/server-actions/index.d.ts +1 -0
- package/cjs/dist/server-actions/index.js +1 -0
- package/cjs/dist/services/buy-now-service.d.ts +346 -0
- package/cjs/dist/services/buy-now-service.js +197 -0
- package/cjs/dist/services/categories-list-service.d.ts +164 -0
- package/cjs/dist/services/categories-list-service.js +148 -0
- package/cjs/dist/services/index.d.ts +5 -0
- package/cjs/dist/services/index.js +5 -0
- package/cjs/dist/services/pay-now-service.d.ts +214 -0
- package/cjs/dist/services/pay-now-service.js +156 -0
- package/cjs/dist/services/product-modifiers-service.d.ts +34 -0
- package/cjs/dist/services/product-modifiers-service.js +107 -0
- package/cjs/dist/services/product-service.d.ts +177 -0
- package/cjs/dist/services/product-service.js +190 -0
- package/cjs/dist/services/products-list-search-service.d.ts +1 -0
- package/cjs/dist/services/products-list-search-service.js +1 -0
- package/cjs/dist/services/products-list-service.d.ts +429 -0
- package/cjs/dist/services/products-list-service.js +893 -0
- package/cjs/dist/services/selected-variant-service.d.ts +66 -0
- package/cjs/dist/services/selected-variant-service.js +527 -0
- package/cjs/dist/utils/index.d.ts +1 -0
- package/cjs/dist/utils/index.js +30 -0
- package/cjs/dist/utils/url-params.d.ts +73 -0
- package/cjs/dist/utils/url-params.js +114 -0
- package/cjs/package.json +3 -0
- package/dist/astro/actions/custom-checkout.d.ts +50 -0
- package/dist/astro/actions/custom-checkout.js +53 -0
- package/dist/astro/actions/index.d.ts +1 -0
- package/dist/astro/actions/index.js +1 -0
- package/dist/data-component-tags.d.ts +8 -0
- package/dist/data-component-tags.js +9 -0
- package/dist/enums/index.d.ts +2 -0
- package/dist/enums/index.js +2 -0
- package/dist/enums/social-platform-enums.d.ts +25 -0
- package/dist/enums/social-platform-enums.js +27 -0
- package/dist/enums/sort-enums.d.ts +17 -0
- package/dist/enums/sort-enums.js +18 -0
- package/dist/react/Category.d.ts +242 -0
- package/dist/react/Category.js +235 -0
- package/dist/react/CategoryList.d.ts +107 -0
- package/dist/react/CategoryList.js +91 -0
- package/dist/react/Choice.d.ts +211 -0
- package/dist/react/Choice.js +213 -0
- package/dist/react/Option.d.ts +242 -0
- package/dist/react/Option.js +346 -0
- package/dist/react/Product.d.ts +1065 -0
- package/dist/react/Product.js +1157 -0
- package/dist/react/ProductList.d.ts +400 -0
- package/dist/react/ProductList.js +368 -0
- package/dist/react/core/CategoryList.d.ts +194 -0
- package/dist/react/core/CategoryList.js +180 -0
- package/dist/react/core/Product.d.ts +225 -0
- package/dist/react/core/Product.js +190 -0
- package/dist/react/core/ProductList.d.ts +235 -0
- package/dist/react/core/ProductList.js +217 -0
- package/dist/react/core/ProductListFilters.d.ts +138 -0
- package/dist/react/core/ProductListFilters.js +242 -0
- package/dist/react/core/ProductListPagination.d.ts +49 -0
- package/dist/react/core/ProductListPagination.js +41 -0
- package/dist/react/core/ProductListSort.d.ts +19 -0
- package/dist/react/core/ProductListSort.js +52 -0
- package/dist/react/core/ProductModifiers.d.ts +416 -0
- package/dist/react/core/ProductModifiers.js +413 -0
- package/dist/react/core/ProductVariantSelector.d.ts +313 -0
- package/dist/react/core/ProductVariantSelector.js +291 -0
- package/dist/react/core/SelectedVariant.d.ts +230 -0
- package/dist/react/core/SelectedVariant.js +269 -0
- package/dist/react/index.d.ts +6 -0
- package/dist/react/index.js +6 -0
- package/dist/react/types.d.ts +8 -0
- package/dist/react/types.js +9 -0
- package/dist/server-actions/custom-checkout-action.d.ts +49 -0
- package/dist/server-actions/custom-checkout-action.js +64 -0
- package/dist/server-actions/index.d.ts +1 -0
- package/dist/server-actions/index.js +1 -0
- package/dist/services/buy-now-service.d.ts +346 -0
- package/dist/services/buy-now-service.js +197 -0
- package/dist/services/categories-list-service.d.ts +164 -0
- package/dist/services/categories-list-service.js +148 -0
- package/dist/services/index.d.ts +5 -0
- package/dist/services/index.js +5 -0
- package/dist/services/pay-now-service.d.ts +214 -0
- package/dist/services/pay-now-service.js +156 -0
- package/dist/services/product-modifiers-service.d.ts +34 -0
- package/dist/services/product-modifiers-service.js +107 -0
- package/dist/services/product-service.d.ts +177 -0
- package/dist/services/product-service.js +190 -0
- package/dist/services/products-list-search-service.d.ts +0 -0
- package/dist/services/products-list-search-service.js +1 -0
- package/dist/services/products-list-service.d.ts +429 -0
- package/dist/services/products-list-service.js +893 -0
- package/dist/services/selected-variant-service.d.ts +66 -0
- package/dist/services/selected-variant-service.js +527 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +30 -0
- package/dist/utils/url-params.d.ts +73 -0
- package/dist/utils/url-params.js +114 -0
- package/package.json +89 -0
- package/react/package.json +4 -0
- package/server-actions/package.json +4 -0
- package/services/package.json +4 -0
|
@@ -0,0 +1,1157 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { InventoryAvailabilityStatus } from '@wix/auto_sdk_stores_products-v-3';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Commerce } from '@wix/headless-ecom/react';
|
|
5
|
+
import { AsChildSlot } from '@wix/headless-utils/react';
|
|
6
|
+
import { MediaGallery } from '@wix/headless-media/react';
|
|
7
|
+
import { Quantity as QuantityComponent } from '@wix/headless-components/react';
|
|
8
|
+
import * as CoreProduct from './core/Product.js';
|
|
9
|
+
import * as CoreProductVariantSelector from './core/ProductVariantSelector.js';
|
|
10
|
+
import * as CoreProductModifiers from './core/ProductModifiers.js';
|
|
11
|
+
import * as CoreSelectedVariant from './core/SelectedVariant.js';
|
|
12
|
+
import * as Option from './Option.js';
|
|
13
|
+
import { AsContent } from './types.js';
|
|
14
|
+
import { DataComponentTags } from '../data-component-tags.js';
|
|
15
|
+
const VariantsContext = React.createContext(null);
|
|
16
|
+
/**
|
|
17
|
+
* Hook to access variants context
|
|
18
|
+
*/
|
|
19
|
+
export function useVariantsContext() {
|
|
20
|
+
const context = React.useContext(VariantsContext);
|
|
21
|
+
if (!context) {
|
|
22
|
+
throw new Error('useVariantsContext must be used within a Product.Variants component');
|
|
23
|
+
}
|
|
24
|
+
return context;
|
|
25
|
+
}
|
|
26
|
+
const ModifiersContext = React.createContext(null);
|
|
27
|
+
/**
|
|
28
|
+
* Hook to access modifiers context
|
|
29
|
+
*/
|
|
30
|
+
export function useModifiersContext() {
|
|
31
|
+
const context = React.useContext(ModifiersContext);
|
|
32
|
+
if (!context) {
|
|
33
|
+
throw new Error('useModifiersContext must be used within a Product.Modifiers component');
|
|
34
|
+
}
|
|
35
|
+
return context;
|
|
36
|
+
}
|
|
37
|
+
var TestIds;
|
|
38
|
+
(function (TestIds) {
|
|
39
|
+
TestIds["productRoot"] = "product-root";
|
|
40
|
+
TestIds["productName"] = "product-name";
|
|
41
|
+
TestIds["productDescription"] = "product-description";
|
|
42
|
+
TestIds["productPrice"] = "product-price";
|
|
43
|
+
TestIds["productCompareAtPrice"] = "product-compare-at-price";
|
|
44
|
+
TestIds["productSlug"] = "product-slug";
|
|
45
|
+
TestIds["productRaw"] = "product-raw";
|
|
46
|
+
TestIds["productRibbon"] = "product-ribbon";
|
|
47
|
+
TestIds["productStock"] = "product-stock";
|
|
48
|
+
TestIds["productVariants"] = "product-variants";
|
|
49
|
+
TestIds["productVariantOptions"] = "product-variant-options";
|
|
50
|
+
TestIds["productVariantSelectorReset"] = "product-variant-selector-reset";
|
|
51
|
+
TestIds["productVariantOption"] = "product-variant-option";
|
|
52
|
+
TestIds["productVariant"] = "product-variant";
|
|
53
|
+
TestIds["productVariantSku"] = "product-variant-sku";
|
|
54
|
+
TestIds["productVariantWeight"] = "product-variant-weight";
|
|
55
|
+
TestIds["productVariantStock"] = "product-variant-stock";
|
|
56
|
+
TestIds["productModifiers"] = "product-modifiers";
|
|
57
|
+
TestIds["productModifierOptions"] = "product-modifier-options";
|
|
58
|
+
TestIds["productModifierOption"] = "product-modifier-option";
|
|
59
|
+
TestIds["productMediaGallery"] = "product-media-gallery";
|
|
60
|
+
TestIds["productAddToCart"] = "product-add-to-cart";
|
|
61
|
+
TestIds["productActionAddToCart"] = "product-action-add-to-cart";
|
|
62
|
+
TestIds["productActionBuyNow"] = "product-action-buy-now";
|
|
63
|
+
TestIds["productActionPreOrder"] = "product-action-can-pre-order";
|
|
64
|
+
TestIds["productQuantity"] = "product-quantity";
|
|
65
|
+
TestIds["productQuantityDecrement"] = "product-quantity-decrement";
|
|
66
|
+
TestIds["productQuantityInput"] = "product-quantity-input";
|
|
67
|
+
TestIds["productQuantityIncrement"] = "product-quantity-increment";
|
|
68
|
+
TestIds["productQuantityRaw"] = "product-quantity-raw";
|
|
69
|
+
})(TestIds || (TestIds = {}));
|
|
70
|
+
/**
|
|
71
|
+
* Root component that provides all necessary service contexts for a complete product experience.
|
|
72
|
+
* This composite component combines Product, ProductVariantSelector, ProductModifiers, and SelectedVariant
|
|
73
|
+
* functionality following the documented API patterns with proper data attributes.
|
|
74
|
+
*
|
|
75
|
+
* @order 1
|
|
76
|
+
* @component
|
|
77
|
+
* @example
|
|
78
|
+
* ```tsx
|
|
79
|
+
* import { Product } from '@wix/stores/components';
|
|
80
|
+
*
|
|
81
|
+
* function ProductPage({ product }) {
|
|
82
|
+
* return (
|
|
83
|
+
* <Product.Root product={product}>
|
|
84
|
+
* <Product.Name className="text-4xl font-bold" />
|
|
85
|
+
* </Product.Root>
|
|
86
|
+
* );
|
|
87
|
+
* }
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export const Root = (props) => {
|
|
91
|
+
const { children, product, ...attrs } = props;
|
|
92
|
+
return (_jsx(CoreProduct.Root, { productServiceConfig: { product: props.product }, children: _jsx(MediaGallery.Root, { mediaGalleryServiceConfig: {
|
|
93
|
+
media: props.product.media?.itemsInfo?.items ?? [],
|
|
94
|
+
}, children: _jsx(CoreProductVariantSelector.Root, { children: _jsx(CoreProductModifiers.Root, { children: _jsx(CoreSelectedVariant.Root, { children: _jsx(AsChildSlot, { "data-testid": TestIds.productRoot, "data-component-tag": DataComponentTags.productRoot, ...attrs, children: children }) }) }) }) }) }));
|
|
95
|
+
};
|
|
96
|
+
Root.displayName = 'Product.Root';
|
|
97
|
+
/**
|
|
98
|
+
* Displays the product name with customizable rendering following the documented API.
|
|
99
|
+
*
|
|
100
|
+
* @component
|
|
101
|
+
* @example
|
|
102
|
+
* ```tsx
|
|
103
|
+
* // Default usage
|
|
104
|
+
* <Product.Name className="text-4xl font-bold" />
|
|
105
|
+
*
|
|
106
|
+
* // asChild with primitive
|
|
107
|
+
* <Product.Name asChild>
|
|
108
|
+
* <h1 className="text-4xl font-bold" />
|
|
109
|
+
* </Product.Name>
|
|
110
|
+
*
|
|
111
|
+
* // asChild with react component
|
|
112
|
+
* <Product.Name asChild>
|
|
113
|
+
* {React.forwardRef(({name, ...props}, ref) => (
|
|
114
|
+
* <h1 ref={ref} {...props} className="text-4xl font-bold">
|
|
115
|
+
* {name}
|
|
116
|
+
* </h1>
|
|
117
|
+
* ))}
|
|
118
|
+
* </Product.Name>
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export const Name = React.forwardRef((props, ref) => {
|
|
122
|
+
const { asChild, children, className, ...otherProps } = props;
|
|
123
|
+
return (_jsx(CoreProduct.Name, { children: ({ name }) => {
|
|
124
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productName, customElement: children, customElementProps: { name }, content: name, ...otherProps, children: _jsx("div", { children: name }) }));
|
|
125
|
+
} }));
|
|
126
|
+
});
|
|
127
|
+
/**
|
|
128
|
+
* Displays the product description with customizable rendering and format options following the documented API.
|
|
129
|
+
*
|
|
130
|
+
* @component
|
|
131
|
+
* @example
|
|
132
|
+
* ```tsx
|
|
133
|
+
* // Default usage (plain text)
|
|
134
|
+
* <Product.Description className="text-content-secondary" />
|
|
135
|
+
*
|
|
136
|
+
* // HTML content
|
|
137
|
+
* <Product.Description as="html" className="prose" />
|
|
138
|
+
*
|
|
139
|
+
* // asChild with custom rendering
|
|
140
|
+
* <Product.Description as="html" asChild>
|
|
141
|
+
* {({ description }) => (
|
|
142
|
+
* <div
|
|
143
|
+
* className="text-content-secondary"
|
|
144
|
+
* dangerouslySetInnerHTML={{ __html: description }}
|
|
145
|
+
* />
|
|
146
|
+
* )}
|
|
147
|
+
* </Product.Description>
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export const Description = React.forwardRef((props, ref) => {
|
|
151
|
+
const { asChild, children, className, as = AsContent.Plain, ...otherProps } = props;
|
|
152
|
+
return (_jsx(CoreProduct.Description, { children: ({ description: richDescription, plainDescription }) => {
|
|
153
|
+
// Determine which description to use based on the 'as' prop
|
|
154
|
+
let description;
|
|
155
|
+
switch (as) {
|
|
156
|
+
case AsContent.Html:
|
|
157
|
+
description = plainDescription || '';
|
|
158
|
+
break;
|
|
159
|
+
case AsContent.Ricos:
|
|
160
|
+
description = JSON.stringify(richDescription) || '';
|
|
161
|
+
break;
|
|
162
|
+
case AsContent.Plain:
|
|
163
|
+
default:
|
|
164
|
+
// For plain text, we'll strip HTML tags from plainDescription
|
|
165
|
+
description = plainDescription
|
|
166
|
+
? plainDescription.replace(/<[^>]*>/g, '')
|
|
167
|
+
: '';
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
// Handle default rendering based on format
|
|
171
|
+
if (!asChild || !children) {
|
|
172
|
+
if (as === AsContent.Html) {
|
|
173
|
+
return (_jsx("div", { ref: ref, className: className, "data-testid": TestIds.productDescription, dangerouslySetInnerHTML: { __html: description } }));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productDescription, customElement: children, customElementProps: { description }, content: description, ...otherProps, children: _jsx("div", { children: description }) }));
|
|
177
|
+
} }));
|
|
178
|
+
});
|
|
179
|
+
/**
|
|
180
|
+
* Displays the current product price with customizable rendering following the documented API.
|
|
181
|
+
*
|
|
182
|
+
* @component
|
|
183
|
+
* @example
|
|
184
|
+
* ```tsx
|
|
185
|
+
* // Default usage
|
|
186
|
+
* <Product.Price className="text-3xl font-bold text-content-primary data-[discounted]:text-brand-primary" />
|
|
187
|
+
*
|
|
188
|
+
* // asChild with primitive
|
|
189
|
+
* <Product.Price asChild>
|
|
190
|
+
* <span className="text-3xl font-bold text-content-primary" />
|
|
191
|
+
* </Product.Price>
|
|
192
|
+
*
|
|
193
|
+
* // asChild with react component
|
|
194
|
+
* <Product.Price asChild>
|
|
195
|
+
* {React.forwardRef(({price, formattedPrice, ...props}, ref) => (
|
|
196
|
+
* <span ref={ref} {...props} className="text-3xl font-bold text-content-primary">
|
|
197
|
+
* {formattedPrice}
|
|
198
|
+
* </span>
|
|
199
|
+
* ))}
|
|
200
|
+
* </Product.Price>
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
export const Price = React.forwardRef((props, ref) => {
|
|
204
|
+
const { asChild, children, className, ...otherProps } = props;
|
|
205
|
+
return (_jsx(CoreSelectedVariant.Price, { children: ({ price, compareAtPrice }) => {
|
|
206
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productPrice, "data-discounted": compareAtPrice !== null, customElement: children, customElementProps: {
|
|
207
|
+
price,
|
|
208
|
+
formattedPrice: price,
|
|
209
|
+
}, content: price, ...otherProps, children: _jsx("span", { children: price }) }));
|
|
210
|
+
} }));
|
|
211
|
+
});
|
|
212
|
+
/**
|
|
213
|
+
* Displays the compare-at (original) price when on sale with customizable rendering following the documented API.
|
|
214
|
+
*
|
|
215
|
+
* @component
|
|
216
|
+
* @example
|
|
217
|
+
* ```tsx
|
|
218
|
+
* // Default usage (only shows when on sale)
|
|
219
|
+
* <Product.CompareAtPrice className="text-lg text-content-faded line-through hidden data-[discounted]:inline" />
|
|
220
|
+
*
|
|
221
|
+
* // asChild with primitive
|
|
222
|
+
* <Product.CompareAtPrice asChild>
|
|
223
|
+
* <span className="text-lg text-content-faded line-through" />
|
|
224
|
+
* </Product.CompareAtPrice>
|
|
225
|
+
*
|
|
226
|
+
* // asChild with react component
|
|
227
|
+
* <Product.CompareAtPrice asChild>
|
|
228
|
+
* {React.forwardRef(({formattedPrice, ...props}, ref) => (
|
|
229
|
+
* <span
|
|
230
|
+
* ref={ref}
|
|
231
|
+
* {...props}
|
|
232
|
+
* className="hidden data-[discounted]:inline text-lg text-content-faded line-through"
|
|
233
|
+
* >
|
|
234
|
+
* Was: {formattedPrice}
|
|
235
|
+
* </span>
|
|
236
|
+
* ))}
|
|
237
|
+
* </Product.CompareAtPrice>
|
|
238
|
+
* ```
|
|
239
|
+
*/
|
|
240
|
+
export const CompareAtPrice = React.forwardRef((props, ref) => {
|
|
241
|
+
const { asChild, children, className, ...otherProps } = props;
|
|
242
|
+
const testId = TestIds.productCompareAtPrice;
|
|
243
|
+
return (_jsx(CoreSelectedVariant.Price, { children: ({ compareAtPrice }) => {
|
|
244
|
+
// Don't render anything if there's no compare-at price
|
|
245
|
+
if (!compareAtPrice) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": testId, "data-discounted": compareAtPrice !== null, customElement: children, customElementProps: {
|
|
249
|
+
price: compareAtPrice,
|
|
250
|
+
formattedPrice: compareAtPrice,
|
|
251
|
+
}, content: compareAtPrice, ...otherProps, children: _jsx("span", { children: compareAtPrice }) }));
|
|
252
|
+
} }));
|
|
253
|
+
});
|
|
254
|
+
/**
|
|
255
|
+
* Product Slug component that displays the product's slug
|
|
256
|
+
*
|
|
257
|
+
* @component
|
|
258
|
+
* @order 6
|
|
259
|
+
* @example
|
|
260
|
+
* ```tsx
|
|
261
|
+
* import { Product } from '@wix/stores/components';
|
|
262
|
+
*
|
|
263
|
+
* function ProductSlugLink() {
|
|
264
|
+
* return (
|
|
265
|
+
* <Product.Slug>
|
|
266
|
+
* {({ slug }) => (
|
|
267
|
+
* <a href={`/product/${slug}`}>
|
|
268
|
+
* View Product Details
|
|
269
|
+
* </a>
|
|
270
|
+
* )}
|
|
271
|
+
* </Product.Slug>
|
|
272
|
+
* );
|
|
273
|
+
* }
|
|
274
|
+
* ```
|
|
275
|
+
*/
|
|
276
|
+
export const Slug = React.forwardRef((props, ref) => {
|
|
277
|
+
const { asChild, children, className, ...otherProps } = props;
|
|
278
|
+
const testId = TestIds.productSlug;
|
|
279
|
+
return (_jsx(CoreProduct.Slug, { children: ({ slug }) => {
|
|
280
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": testId, customElement: children, customElementProps: { slug }, content: slug, ...otherProps, children: _jsx("span", { children: slug }) }));
|
|
281
|
+
} }));
|
|
282
|
+
});
|
|
283
|
+
/**
|
|
284
|
+
* Provides access to the raw product data for advanced use cases.
|
|
285
|
+
* Similar to Category.Raw, this should only be used when you need custom access to product data.
|
|
286
|
+
*
|
|
287
|
+
* @component
|
|
288
|
+
* @example
|
|
289
|
+
* ```tsx
|
|
290
|
+
* // Custom rendering with forwardRef
|
|
291
|
+
* <Product.Raw asChild>
|
|
292
|
+
* {React.forwardRef(({product, ...props}, ref) => (
|
|
293
|
+
* <div ref={ref} {...props} className="product-debug">
|
|
294
|
+
* <span>Product ID: {product._id}</span>
|
|
295
|
+
* <span>SKU: {product.sku}</span>
|
|
296
|
+
* <span>Inventory: {product.inventory?.quantity}</span>
|
|
297
|
+
* </div>
|
|
298
|
+
* ))}
|
|
299
|
+
* </Product.Raw>
|
|
300
|
+
* ```
|
|
301
|
+
*/
|
|
302
|
+
export const Raw = React.forwardRef((props, ref) => {
|
|
303
|
+
const { asChild, children, className, ...otherProps } = props;
|
|
304
|
+
return (_jsx(CoreProduct.Content, { children: ({ product }) => {
|
|
305
|
+
// Raw component should not render anything by default when not using asChild
|
|
306
|
+
if (!asChild || !children) {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productRaw, customElement: children, customElementProps: { product }, ...otherProps }));
|
|
310
|
+
} }));
|
|
311
|
+
});
|
|
312
|
+
/**
|
|
313
|
+
* Displays the product ribbon with customizable rendering following the documented API.
|
|
314
|
+
*
|
|
315
|
+
* @component
|
|
316
|
+
* @example
|
|
317
|
+
* ```tsx
|
|
318
|
+
* // Default usage
|
|
319
|
+
* <Product.Ribbon className="absolute top-2 left-2 bg-red-500 text-white px-2 py-1 text-xs rounded" />
|
|
320
|
+
*
|
|
321
|
+
* // asChild with primitive
|
|
322
|
+
* <Product.Ribbon asChild>
|
|
323
|
+
* <span className="ribbon-badge" />
|
|
324
|
+
* </Product.Ribbon>
|
|
325
|
+
*
|
|
326
|
+
* // asChild with react component
|
|
327
|
+
* <Product.Ribbon asChild>
|
|
328
|
+
* {React.forwardRef(({ribbon, ...props}, ref) => (
|
|
329
|
+
* <div ref={ref} {...props} className="ribbon-badge">
|
|
330
|
+
* {ribbon}
|
|
331
|
+
* </div>
|
|
332
|
+
* ))}
|
|
333
|
+
* </Product.Ribbon>
|
|
334
|
+
* ```
|
|
335
|
+
*/
|
|
336
|
+
export const Ribbon = React.forwardRef((props, ref) => {
|
|
337
|
+
const { asChild, children, className } = props;
|
|
338
|
+
return (_jsx(CoreProduct.Ribbon, { children: ({ ribbon, hasRibbon }) => {
|
|
339
|
+
// Don't render anything if there's no ribbon
|
|
340
|
+
if (!hasRibbon) {
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productRibbon, customElement: children, customElementProps: { ribbon }, content: ribbon, children: _jsx("span", { children: ribbon }) }));
|
|
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 const Stock = React.forwardRef((props, ref) => {
|
|
392
|
+
const { asChild, children, className, labels } = props;
|
|
393
|
+
return (_jsx(CoreProduct.Content, { children: ({ product }) => {
|
|
394
|
+
const availabilityStatus = product.inventory?.availabilityStatus;
|
|
395
|
+
// Default labels
|
|
396
|
+
const defaultLabels = {
|
|
397
|
+
inStock: 'In Stock',
|
|
398
|
+
limitedStock: 'Partially Out of Stock',
|
|
399
|
+
outOfStock: 'Out of Stock',
|
|
400
|
+
};
|
|
401
|
+
const finalLabels = { ...defaultLabels, ...labels };
|
|
402
|
+
// Determine status based on availabilityStatus
|
|
403
|
+
let status;
|
|
404
|
+
let label;
|
|
405
|
+
switch (availabilityStatus) {
|
|
406
|
+
case InventoryAvailabilityStatus.IN_STOCK:
|
|
407
|
+
status = 'in-stock';
|
|
408
|
+
label = finalLabels.inStock;
|
|
409
|
+
break;
|
|
410
|
+
case InventoryAvailabilityStatus.PARTIALLY_OUT_OF_STOCK:
|
|
411
|
+
status = 'limited-stock';
|
|
412
|
+
label = finalLabels.limitedStock;
|
|
413
|
+
break;
|
|
414
|
+
case InventoryAvailabilityStatus.OUT_OF_STOCK:
|
|
415
|
+
default:
|
|
416
|
+
status = 'out-of-stock';
|
|
417
|
+
label = finalLabels.outOfStock;
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productStock, "data-state": status, customElement: children, customElementProps: {
|
|
421
|
+
status,
|
|
422
|
+
label,
|
|
423
|
+
}, content: label, children: _jsx("span", { children: label }) }));
|
|
424
|
+
} }));
|
|
425
|
+
});
|
|
426
|
+
/**
|
|
427
|
+
* Container for product variant selection system.
|
|
428
|
+
* Does not render when there are no variants.
|
|
429
|
+
*
|
|
430
|
+
* @component
|
|
431
|
+
* @example
|
|
432
|
+
* ```tsx
|
|
433
|
+
* // Default usage
|
|
434
|
+
* <Product.Variants>
|
|
435
|
+
* <Product.VariantOptions>
|
|
436
|
+
* <Product.VariantOptionRepeater>
|
|
437
|
+
* <Option.Name className="text-lg font-medium mb-3" />
|
|
438
|
+
* <Option.Choices>
|
|
439
|
+
* <Option.ChoiceRepeater>
|
|
440
|
+
* <Choice.Text className="px-4 py-2 border rounded-lg" />
|
|
441
|
+
* <Choice.Color className="w-10 h-10 rounded-full border-4" />
|
|
442
|
+
* </Option.ChoiceRepeater>
|
|
443
|
+
* </Option.Choices>
|
|
444
|
+
* </Product.VariantOptionRepeater>
|
|
445
|
+
* </Product.VariantOptions>
|
|
446
|
+
* </Product.Variants>
|
|
447
|
+
*
|
|
448
|
+
* // asChild with primitive
|
|
449
|
+
* <Product.Variants asChild>
|
|
450
|
+
* <section className="variant-section">
|
|
451
|
+
* <Product.VariantOptions>
|
|
452
|
+
* // variant options
|
|
453
|
+
* </Product.VariantOptions>
|
|
454
|
+
* </section>
|
|
455
|
+
* </Product.Variants>
|
|
456
|
+
*
|
|
457
|
+
* // asChild with react component
|
|
458
|
+
* <Product.Variants asChild>
|
|
459
|
+
* {React.forwardRef(({hasOptions, ...props}, ref) => (
|
|
460
|
+
* <section ref={ref} {...props} className="variant-section">
|
|
461
|
+
* {hasOptions && <h3>Choose Options</h3>}
|
|
462
|
+
* {props.children}
|
|
463
|
+
* </section>
|
|
464
|
+
* ))}
|
|
465
|
+
* </Product.Variants>
|
|
466
|
+
* ```
|
|
467
|
+
*/
|
|
468
|
+
export const Variants = React.forwardRef((props, ref) => {
|
|
469
|
+
const { asChild, children, className, ...attributes } = props;
|
|
470
|
+
return (_jsx(CoreProductVariantSelector.Options, { children: ({ hasOptions, options }) => {
|
|
471
|
+
if (!hasOptions)
|
|
472
|
+
return null;
|
|
473
|
+
const contextValue = {
|
|
474
|
+
hasOptions,
|
|
475
|
+
options,
|
|
476
|
+
};
|
|
477
|
+
return (_jsx(VariantsContext.Provider, { value: contextValue, children: _jsx(AsChildSlot, { ...attributes, ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productVariants, customElement: children, children: _jsx("div", { children: React.isValidElement(children) ? children : null }) }) }));
|
|
478
|
+
} }));
|
|
479
|
+
});
|
|
480
|
+
/**
|
|
481
|
+
* Component that provides access to variant options.
|
|
482
|
+
*
|
|
483
|
+
* @component
|
|
484
|
+
* @example
|
|
485
|
+
* ```tsx
|
|
486
|
+
* // Default usage
|
|
487
|
+
* <Product.VariantOptions emptyState={<div>No options available</div>}>
|
|
488
|
+
* <Product.VariantOptionRepeater>
|
|
489
|
+
* <Option.Name />
|
|
490
|
+
* <Option.Choices>
|
|
491
|
+
* <Option.ChoiceRepeater>
|
|
492
|
+
* <Choice.Text />
|
|
493
|
+
* </Option.ChoiceRepeater>
|
|
494
|
+
* </Option.Choices>
|
|
495
|
+
* </Product.VariantOptionRepeater>
|
|
496
|
+
* </Product.VariantOptions>
|
|
497
|
+
*
|
|
498
|
+
* // Simple container usage
|
|
499
|
+
* <Product.VariantOptions emptyState={<div>No options</div>}>
|
|
500
|
+
* <div className="options-container">
|
|
501
|
+
* <Product.VariantOptionRepeater>
|
|
502
|
+
* // option content
|
|
503
|
+
* </Product.VariantOptionRepeater>
|
|
504
|
+
* </div>
|
|
505
|
+
* </Product.VariantOptions>
|
|
506
|
+
* ```
|
|
507
|
+
*/
|
|
508
|
+
export const VariantOptions = React.forwardRef((props, ref) => {
|
|
509
|
+
const { children, emptyState } = props;
|
|
510
|
+
const { hasOptions } = useVariantsContext();
|
|
511
|
+
if (!hasOptions) {
|
|
512
|
+
return emptyState || null;
|
|
513
|
+
}
|
|
514
|
+
const attributes = {
|
|
515
|
+
'data-testid': TestIds.productVariantOptions,
|
|
516
|
+
};
|
|
517
|
+
return (_jsx("div", { ...attributes, ref: ref, children: children }));
|
|
518
|
+
});
|
|
519
|
+
/**
|
|
520
|
+
* Repeater component that renders children for each variant option.
|
|
521
|
+
*
|
|
522
|
+
* @component
|
|
523
|
+
*/
|
|
524
|
+
export const VariantOptionRepeater = React.forwardRef((props, _ref) => {
|
|
525
|
+
const { children } = props;
|
|
526
|
+
const { hasOptions, options } = useVariantsContext();
|
|
527
|
+
if (!hasOptions)
|
|
528
|
+
return null;
|
|
529
|
+
return (_jsx(_Fragment, { children: options.map((option) => {
|
|
530
|
+
return (_jsx(CoreProductVariantSelector.Option, { option: option, children: (optionData) => (_jsx(Option.Root, { option: {
|
|
531
|
+
...optionData,
|
|
532
|
+
mandatory: false,
|
|
533
|
+
}, children: children })) }, option._id));
|
|
534
|
+
}) }));
|
|
535
|
+
});
|
|
536
|
+
/**
|
|
537
|
+
* Container for product modifier system.
|
|
538
|
+
* Does not render when there are no modifiers.
|
|
539
|
+
*
|
|
540
|
+
* @component
|
|
541
|
+
* @example
|
|
542
|
+
* ```tsx
|
|
543
|
+
* // Default usage
|
|
544
|
+
* <Product.Modifiers>
|
|
545
|
+
* <Product.ModifierOptions>
|
|
546
|
+
* <Product.ModifierOptionRepeater>
|
|
547
|
+
* <Option.Name className="text-lg font-medium mb-3" />
|
|
548
|
+
* <Option.Choices>
|
|
549
|
+
* <Option.ChoiceRepeater>
|
|
550
|
+
* <Choice.Text className="px-4 py-2 border rounded-lg" />
|
|
551
|
+
* <Choice.Color className="w-10 h-10 rounded-full border-4" />
|
|
552
|
+
* <Choice.FreeText className="w-full p-3 border rounded-lg resize-none" />
|
|
553
|
+
* </Option.ChoiceRepeater>
|
|
554
|
+
* </Option.Choices>
|
|
555
|
+
* </Product.ModifierOptionRepeater>
|
|
556
|
+
* </Product.ModifierOptions>
|
|
557
|
+
* </Product.Modifiers>
|
|
558
|
+
*
|
|
559
|
+
* // asChild with primitive
|
|
560
|
+
* <Product.Modifiers asChild>
|
|
561
|
+
* <section className="modifier-section">
|
|
562
|
+
* <Product.ModifierOptions>
|
|
563
|
+
* // modifier options
|
|
564
|
+
* </Product.ModifierOptions>
|
|
565
|
+
* </section>
|
|
566
|
+
* </Product.Modifiers>
|
|
567
|
+
*
|
|
568
|
+
* // asChild with react component
|
|
569
|
+
* <Product.Modifiers asChild>
|
|
570
|
+
* {React.forwardRef(({hasModifiers, ...props}, ref) => (
|
|
571
|
+
* <section ref={ref} {...props} className="modifier-section">
|
|
572
|
+
* {hasModifiers && <h3>Customize Your Product</h3>}
|
|
573
|
+
* {props.children}
|
|
574
|
+
* </section>
|
|
575
|
+
* ))}
|
|
576
|
+
* </Product.Modifiers>
|
|
577
|
+
* ```
|
|
578
|
+
*/
|
|
579
|
+
export const Modifiers = React.forwardRef((props, ref) => {
|
|
580
|
+
const { asChild, children, className, ...attributes } = props;
|
|
581
|
+
return (_jsx(CoreProductModifiers.Modifiers, { children: ({ hasModifiers, modifiers }) => {
|
|
582
|
+
if (!hasModifiers)
|
|
583
|
+
return null;
|
|
584
|
+
const contextValue = {
|
|
585
|
+
hasModifiers,
|
|
586
|
+
modifiers,
|
|
587
|
+
};
|
|
588
|
+
return (_jsx(ModifiersContext.Provider, { value: contextValue, children: _jsx(AsChildSlot, { ...attributes, ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productModifiers, customElement: children, children: _jsx("div", { children: React.isValidElement(children) ? children : null }) }) }));
|
|
589
|
+
} }));
|
|
590
|
+
});
|
|
591
|
+
/**
|
|
592
|
+
* Component that provides access to modifier options.
|
|
593
|
+
*
|
|
594
|
+
* @component
|
|
595
|
+
* @example
|
|
596
|
+
* ```tsx
|
|
597
|
+
* // Default usage
|
|
598
|
+
* <Product.ModifierOptions emptyState={<div>No options available</div>}>
|
|
599
|
+
* <Product.ModifierOptionRepeater>
|
|
600
|
+
* <Option.Name />
|
|
601
|
+
* <Option.Choices>
|
|
602
|
+
* <Option.ChoiceRepeater>
|
|
603
|
+
* <Choice.Text />
|
|
604
|
+
* <Choice.Color />
|
|
605
|
+
* <Choice.FreeText />
|
|
606
|
+
* </Option.ChoiceRepeater>
|
|
607
|
+
* </Option.Choices>
|
|
608
|
+
* </Product.ModifierOptionRepeater>
|
|
609
|
+
* </Product.ModifierOptions>
|
|
610
|
+
*
|
|
611
|
+
* // Simple container usage
|
|
612
|
+
* <Product.ModifierOptions emptyState={<div>No options</div>}>
|
|
613
|
+
* <div className="options-container">
|
|
614
|
+
* <Product.ModifierOptionRepeater>
|
|
615
|
+
* // option content
|
|
616
|
+
* </Product.ModifierOptionRepeater>
|
|
617
|
+
* </div>
|
|
618
|
+
* </Product.ModifierOptions>
|
|
619
|
+
* ```
|
|
620
|
+
*/
|
|
621
|
+
export const ModifierOptions = React.forwardRef((props, ref) => {
|
|
622
|
+
const { children, emptyState } = props;
|
|
623
|
+
const { hasModifiers } = useModifiersContext();
|
|
624
|
+
if (!hasModifiers) {
|
|
625
|
+
return emptyState || null;
|
|
626
|
+
}
|
|
627
|
+
const attributes = {
|
|
628
|
+
'data-testid': TestIds.productModifierOptions,
|
|
629
|
+
};
|
|
630
|
+
return (_jsx("div", { ...attributes, ref: ref, children: children }));
|
|
631
|
+
});
|
|
632
|
+
/**
|
|
633
|
+
* Repeater component that renders children for each modifier option.
|
|
634
|
+
*
|
|
635
|
+
* @component
|
|
636
|
+
*/
|
|
637
|
+
export const ModifierOptionRepeater = React.forwardRef((props, _ref) => {
|
|
638
|
+
const { children, allowedTypes = ['color', 'text', 'free-text'] } = props;
|
|
639
|
+
const { hasModifiers, modifiers } = useModifiersContext();
|
|
640
|
+
if (!hasModifiers)
|
|
641
|
+
return null;
|
|
642
|
+
return (_jsx(_Fragment, { children: modifiers.map((modifier) => {
|
|
643
|
+
return (_jsx(CoreProductModifiers.Modifier, { modifier: modifier, children: (modifierData) => (_jsx(Option.Root, { option: {
|
|
644
|
+
...modifierData,
|
|
645
|
+
}, allowedTypes: allowedTypes, children: children })) }, modifier._id));
|
|
646
|
+
}) }));
|
|
647
|
+
});
|
|
648
|
+
/**
|
|
649
|
+
* Container for product media gallery.
|
|
650
|
+
* Renders a MediaGallery.Root with the product media items.
|
|
651
|
+
*
|
|
652
|
+
* @component
|
|
653
|
+
* @example
|
|
654
|
+
* ```tsx
|
|
655
|
+
* // Default usage
|
|
656
|
+
* <Product.MediaGallery
|
|
657
|
+
* infinite={true}
|
|
658
|
+
* autoPlay={{ direction: "forward", intervalMs: 5000 }}
|
|
659
|
+
* >
|
|
660
|
+
* <MediaGallery.Viewport />
|
|
661
|
+
* <MediaGallery.Previous />
|
|
662
|
+
* <MediaGallery.Next />
|
|
663
|
+
* <MediaGallery.Thumbnails>
|
|
664
|
+
* <MediaGallery.ThumbnailRepeater>
|
|
665
|
+
* <MediaGallery.ThumbnailItem />
|
|
666
|
+
* </MediaGallery.ThumbnailRepeater>
|
|
667
|
+
* </MediaGallery.Thumbnails>
|
|
668
|
+
* </Product.MediaGallery>
|
|
669
|
+
*
|
|
670
|
+
* // Simple usage
|
|
671
|
+
* <Product.MediaGallery>
|
|
672
|
+
* <MediaGallery.Viewport className="rounded-lg" />
|
|
673
|
+
* </Product.MediaGallery>
|
|
674
|
+
* ```
|
|
675
|
+
*/
|
|
676
|
+
export const ProductMediaGallery = React.forwardRef((props, ref) => {
|
|
677
|
+
const { children, infinite, autoPlay, ...otherProps } = props;
|
|
678
|
+
return (_jsx(CoreProduct.Media, { children: ({ mediaItems, mainMedia }) => {
|
|
679
|
+
const media = mediaItems.length > 0 ? mediaItems : mainMedia ? [mainMedia] : [];
|
|
680
|
+
const mediaGalleryServiceConfig = {
|
|
681
|
+
media,
|
|
682
|
+
infinite,
|
|
683
|
+
autoPlay,
|
|
684
|
+
};
|
|
685
|
+
const attributes = {
|
|
686
|
+
'data-testid': TestIds.productMediaGallery,
|
|
687
|
+
};
|
|
688
|
+
return (_jsx("div", { ...attributes, ref: ref, ...otherProps, children: _jsx(MediaGallery.Root, { mediaGalleryServiceConfig: mediaGalleryServiceConfig, children: children }) }));
|
|
689
|
+
} }));
|
|
690
|
+
});
|
|
691
|
+
/**
|
|
692
|
+
* Alias for ProductMediaGallery to match the documented API
|
|
693
|
+
*/
|
|
694
|
+
export { ProductMediaGallery as MediaGallery };
|
|
695
|
+
/**
|
|
696
|
+
* Product quantity selector component that integrates with the selected variant.
|
|
697
|
+
* Provides quantity controls with stock validation and can-pre-order support.
|
|
698
|
+
* Uses a compound component pattern with Root, Decrement, Input, Increment, and Raw sub-components.
|
|
699
|
+
*
|
|
700
|
+
* @component
|
|
701
|
+
* @example
|
|
702
|
+
* ```tsx
|
|
703
|
+
* // Compound component usage (recommended)
|
|
704
|
+
* <Product.Quantity.Root className="flex items-center gap-3">
|
|
705
|
+
* <div className="flex items-center border border-brand-light rounded-lg">
|
|
706
|
+
* <Product.Quantity.Decrement className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
707
|
+
* <Product.Quantity.Input className="w-16 text-center py-1 border-x border-brand-light focus:outline-none focus:ring-2 focus:ring-brand-primary" />
|
|
708
|
+
* <Product.Quantity.Increment className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
709
|
+
* </div>
|
|
710
|
+
* <Product.Quantity.Raw asChild>
|
|
711
|
+
* {({ availableQuantity, inStock, isPreOrderEnabled }) => (
|
|
712
|
+
* <div>
|
|
713
|
+
* {!inStock && isPreOrderEnabled && availableQuantity && (
|
|
714
|
+
* <span className="text-content-muted text-sm">
|
|
715
|
+
* Max: {availableQuantity} Pre Order
|
|
716
|
+
* </span>
|
|
717
|
+
* )}
|
|
718
|
+
* {inStock && availableQuantity && availableQuantity < 10 && (
|
|
719
|
+
* <span className="text-content-muted text-sm">
|
|
720
|
+
* Only {availableQuantity} left in stock
|
|
721
|
+
* </span>
|
|
722
|
+
* )}
|
|
723
|
+
* </div>
|
|
724
|
+
* )}
|
|
725
|
+
* </Product.Quantity.Raw>
|
|
726
|
+
* </Product.Quantity.Root>
|
|
727
|
+
*
|
|
728
|
+
* // Legacy asChild usage (still supported)
|
|
729
|
+
* <Product.Quantity asChild>
|
|
730
|
+
* {({ selectedQuantity, availableQuantity, inStock, setSelectedQuantity }) => (
|
|
731
|
+
* <div className="flex items-center gap-3">
|
|
732
|
+
* <div className="flex items-center border border-brand-light rounded-lg">
|
|
733
|
+
* <button
|
|
734
|
+
* onClick={() => setSelectedQuantity(selectedQuantity - 1)}
|
|
735
|
+
* disabled={selectedQuantity <= 1 || (!inStock && !isPreOrderEnabled)}
|
|
736
|
+
* className="px-3 py-2 hover:bg-surface-primary disabled:opacity-50"
|
|
737
|
+
* >
|
|
738
|
+
* -
|
|
739
|
+
* </button>
|
|
740
|
+
* <span className="px-4 py-2 border-x border-brand-light min-w-[3rem] text-center">
|
|
741
|
+
* {selectedQuantity}
|
|
742
|
+
* </span>
|
|
743
|
+
* <button
|
|
744
|
+
* onClick={() => setSelectedQuantity(selectedQuantity + 1)}
|
|
745
|
+
* disabled={availableQuantity && selectedQuantity >= availableQuantity}
|
|
746
|
+
* className="px-3 py-2 hover:bg-surface-primary disabled:opacity-50"
|
|
747
|
+
* >
|
|
748
|
+
* +
|
|
749
|
+
* </button>
|
|
750
|
+
* </div>
|
|
751
|
+
* </div>
|
|
752
|
+
* )}
|
|
753
|
+
* </Product.Quantity>
|
|
754
|
+
* ```
|
|
755
|
+
*/
|
|
756
|
+
export const ProductQuantity = React.forwardRef((props, ref) => {
|
|
757
|
+
const { asChild, children, className } = props;
|
|
758
|
+
return (_jsx(CoreProductVariantSelector.Stock, { children: ({ inStock, isPreOrderEnabled, availableQuantity, selectedQuantity, setSelectedQuantity, }) => {
|
|
759
|
+
const renderProps = {
|
|
760
|
+
selectedQuantity,
|
|
761
|
+
availableQuantity,
|
|
762
|
+
inStock,
|
|
763
|
+
isPreOrderEnabled,
|
|
764
|
+
setSelectedQuantity,
|
|
765
|
+
};
|
|
766
|
+
if (asChild && children) {
|
|
767
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productQuantity, customElement: children, customElementProps: renderProps }));
|
|
768
|
+
}
|
|
769
|
+
return (_jsx(QuantityComponent.Root, { initialValue: selectedQuantity, onValueChange: setSelectedQuantity, className: className, ref: ref, "data-testid": TestIds.productQuantity, children: React.isValidElement(children) ? children : null }));
|
|
770
|
+
} }));
|
|
771
|
+
});
|
|
772
|
+
/**
|
|
773
|
+
* Product Quantity Decrement component.
|
|
774
|
+
* Automatically handles disabled state based on stock and can-pre-order settings.
|
|
775
|
+
* Must be used within Product.Quantity.Root.
|
|
776
|
+
*
|
|
777
|
+
* @component
|
|
778
|
+
* @example
|
|
779
|
+
* ```tsx
|
|
780
|
+
* <Product.Quantity.Decrement className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
781
|
+
* ```
|
|
782
|
+
*/
|
|
783
|
+
export const ProductQuantityDecrement = React.forwardRef((props, ref) => {
|
|
784
|
+
const { asChild, children, className } = props;
|
|
785
|
+
return (_jsx(CoreProductVariantSelector.Stock, { children: ({ selectedQuantity, inStock, isPreOrderEnabled }) => {
|
|
786
|
+
const disabled = selectedQuantity <= 1 || (!inStock && !isPreOrderEnabled);
|
|
787
|
+
if (asChild && children) {
|
|
788
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productQuantityDecrement, customElement: children, customElementProps: { disabled } }));
|
|
789
|
+
}
|
|
790
|
+
return (_jsx(QuantityComponent.Decrement, { className: className, ref: ref, "data-testid": TestIds.productQuantityDecrement, disabled }));
|
|
791
|
+
} }));
|
|
792
|
+
});
|
|
793
|
+
/**
|
|
794
|
+
* Product Quantity Input component.
|
|
795
|
+
* Displays the current quantity value. Must be used within Product.Quantity.Root.
|
|
796
|
+
*
|
|
797
|
+
* @component
|
|
798
|
+
* @example
|
|
799
|
+
* ```tsx
|
|
800
|
+
* <Product.Quantity.Input className="w-16 text-center py-1 border-x border-brand-light focus:outline-none focus:ring-2 focus:ring-brand-primary" />
|
|
801
|
+
* ```
|
|
802
|
+
*/
|
|
803
|
+
export const ProductQuantityInput = React.forwardRef((props, ref) => {
|
|
804
|
+
const { asChild, children, className, disabled = true } = props;
|
|
805
|
+
return (_jsx(CoreProductVariantSelector.Stock, { children: () => {
|
|
806
|
+
if (asChild && children) {
|
|
807
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, disabled: disabled, "data-testid": TestIds.productQuantityInput, customElement: children, customElementProps: {} }));
|
|
808
|
+
}
|
|
809
|
+
return (_jsx(QuantityComponent.Input, { className: className, disabled: disabled, ref: ref, "data-testid": TestIds.productQuantityInput }));
|
|
810
|
+
} }));
|
|
811
|
+
});
|
|
812
|
+
/**
|
|
813
|
+
* Product Quantity Increment component.
|
|
814
|
+
* Automatically handles disabled state based on stock availability.
|
|
815
|
+
* Must be used within Product.Quantity.Root.
|
|
816
|
+
*
|
|
817
|
+
* @component
|
|
818
|
+
* @example
|
|
819
|
+
* ```tsx
|
|
820
|
+
* <Product.Quantity.Increment className="px-3 py-1 hover:bg-surface-primary transition-colors" />
|
|
821
|
+
* ```
|
|
822
|
+
*/
|
|
823
|
+
export const ProductQuantityIncrement = React.forwardRef((props, ref) => {
|
|
824
|
+
const { asChild, children, className } = props;
|
|
825
|
+
return (_jsx(CoreProductVariantSelector.Stock, { children: ({ selectedQuantity, availableQuantity, inStock, isPreOrderEnabled, }) => {
|
|
826
|
+
const disabled = (!!availableQuantity && selectedQuantity >= availableQuantity) ||
|
|
827
|
+
(!inStock && !isPreOrderEnabled);
|
|
828
|
+
if (asChild && children) {
|
|
829
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productQuantityIncrement, customElement: children, customElementProps: { disabled } }));
|
|
830
|
+
}
|
|
831
|
+
return (_jsx(QuantityComponent.Increment, { className: className, ref: ref, "data-testid": TestIds.productQuantityIncrement, disabled }));
|
|
832
|
+
} }));
|
|
833
|
+
});
|
|
834
|
+
/**
|
|
835
|
+
* Product Quantity Raw component.
|
|
836
|
+
* Provides access to raw quantity data for custom stock messages and advanced use cases.
|
|
837
|
+
* Must be used within Product.Quantity.Root.
|
|
838
|
+
*
|
|
839
|
+
* @component
|
|
840
|
+
* @example
|
|
841
|
+
* ```tsx
|
|
842
|
+
* <Product.Quantity.Raw asChild>
|
|
843
|
+
* {({ availableQuantity, inStock, isPreOrderEnabled }) => (
|
|
844
|
+
* <div>
|
|
845
|
+
* {!inStock && isPreOrderEnabled && availableQuantity && (
|
|
846
|
+
* <span className="text-content-muted text-sm">
|
|
847
|
+
* Max: {availableQuantity} Pre Order
|
|
848
|
+
* </span>
|
|
849
|
+
* )}
|
|
850
|
+
* {inStock && availableQuantity && availableQuantity < 10 && (
|
|
851
|
+
* <span className="text-content-muted text-sm">
|
|
852
|
+
* Only {availableQuantity} left in stock
|
|
853
|
+
* </span>
|
|
854
|
+
* )}
|
|
855
|
+
* </div>
|
|
856
|
+
* )}
|
|
857
|
+
* </Product.Quantity.Raw>
|
|
858
|
+
* ```
|
|
859
|
+
*/
|
|
860
|
+
export const ProductQuantityRaw = React.forwardRef((props, ref) => {
|
|
861
|
+
const { asChild, children, className } = props;
|
|
862
|
+
return (_jsx(CoreProductVariantSelector.Stock, { children: (renderProps) => {
|
|
863
|
+
return (_jsx(AsChildSlot, { ref: ref, customElement: children, asChild: asChild, className: className, "data-testid": TestIds.productQuantityRaw, customElementProps: renderProps }));
|
|
864
|
+
} }));
|
|
865
|
+
});
|
|
866
|
+
export const ProductVariantSelectorReset = React.forwardRef((props, ref) => {
|
|
867
|
+
const { asChild, children, className } = props;
|
|
868
|
+
return (_jsx(CoreProductVariantSelector.Reset, { children: (renderProps) => {
|
|
869
|
+
if (!renderProps.hasSelections) {
|
|
870
|
+
return null;
|
|
871
|
+
}
|
|
872
|
+
const label = props.label || 'Reset Selections';
|
|
873
|
+
return (_jsx(AsChildSlot, { ref: ref, customElement: children, asChild: asChild, className: className, "data-testid": TestIds.productVariantSelectorReset, customElementProps: renderProps, children: _jsx("button", { onClick: renderProps.reset, children: label }) }));
|
|
874
|
+
} }));
|
|
875
|
+
});
|
|
876
|
+
/**
|
|
877
|
+
* Add to cart action button component following the documented API.
|
|
878
|
+
* Automatically integrates with the selected variant and handles loading states.
|
|
879
|
+
*/
|
|
880
|
+
export const ProductActionAddToCart = React.forwardRef((props, ref) => {
|
|
881
|
+
const { asChild, children, className, label, loadingState } = props;
|
|
882
|
+
return (_jsx(CoreSelectedVariant.Actions, { children: ({ lineItems, canAddToCart, isLoading, addToCart, isPreOrderEnabled, }) => {
|
|
883
|
+
if (isPreOrderEnabled) {
|
|
884
|
+
return null;
|
|
885
|
+
}
|
|
886
|
+
const onClick = addToCart;
|
|
887
|
+
const disabled = !canAddToCart || isLoading;
|
|
888
|
+
if (asChild && children) {
|
|
889
|
+
return (_jsx(Commerce.Actions.AddToCart, { lineItems: lineItems, asChild: asChild, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionAddToCart, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
890
|
+
disabled,
|
|
891
|
+
isLoading,
|
|
892
|
+
onClick,
|
|
893
|
+
} }) }));
|
|
894
|
+
}
|
|
895
|
+
return (_jsx(Commerce.Actions.AddToCart, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, "data-testid": TestIds.productActionAddToCart, "data-in-progress": isLoading }));
|
|
896
|
+
} }));
|
|
897
|
+
});
|
|
898
|
+
/**
|
|
899
|
+
* Buy Now action button component following the documented API.
|
|
900
|
+
* Bypasses the cart and redirects directly to checkout.
|
|
901
|
+
*/
|
|
902
|
+
export const ProductActionBuyNow = React.forwardRef((props, ref) => {
|
|
903
|
+
const { asChild, children, className, label, loadingState } = props;
|
|
904
|
+
return (_jsx(CoreSelectedVariant.Actions, { children: ({ lineItems, canAddToCart, isLoading, buyNow, inStock, isPreOrderEnabled, }) => {
|
|
905
|
+
if (!inStock || isPreOrderEnabled) {
|
|
906
|
+
return null;
|
|
907
|
+
}
|
|
908
|
+
const onClick = buyNow;
|
|
909
|
+
const disabled = !canAddToCart || isLoading;
|
|
910
|
+
if (asChild && children) {
|
|
911
|
+
return (_jsx(Commerce.Actions.BuyNow, { lineItems: lineItems, asChild: asChild, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionBuyNow, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
912
|
+
disabled,
|
|
913
|
+
isLoading,
|
|
914
|
+
onClick,
|
|
915
|
+
} }) }));
|
|
916
|
+
}
|
|
917
|
+
return (_jsx(Commerce.Actions.BuyNow, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, "data-testid": TestIds.productActionBuyNow, "data-in-progress": isLoading }));
|
|
918
|
+
} }));
|
|
919
|
+
});
|
|
920
|
+
/**
|
|
921
|
+
* Pre-Order action button component following the documented API.
|
|
922
|
+
* Handles can-pre-order functionality when products are not in stock.
|
|
923
|
+
*/
|
|
924
|
+
export const ProductActionPreOrder = React.forwardRef((props, ref) => {
|
|
925
|
+
const { asChild, children, className, label, loadingState } = props;
|
|
926
|
+
return (_jsx(CoreSelectedVariant.Actions, { children: ({ lineItems, isLoading, addToCart, isPreOrderEnabled }) => {
|
|
927
|
+
if (!isPreOrderEnabled) {
|
|
928
|
+
return null;
|
|
929
|
+
}
|
|
930
|
+
const canPreOrder = !isLoading;
|
|
931
|
+
const onClick = addToCart;
|
|
932
|
+
const disabled = !canPreOrder;
|
|
933
|
+
if (asChild && children) {
|
|
934
|
+
return (_jsx(Commerce.Actions.AddToCart, { lineItems: lineItems, asChild: asChild, children: _jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productActionPreOrder, "data-in-progress": isLoading, customElement: children, customElementProps: {
|
|
935
|
+
disabled,
|
|
936
|
+
isLoading,
|
|
937
|
+
onClick,
|
|
938
|
+
} }) }));
|
|
939
|
+
}
|
|
940
|
+
return (_jsx(Commerce.Actions.AddToCart, { ref: ref, label: label, lineItems: lineItems, className: className, disabled: disabled, loadingState: loadingState, "data-testid": TestIds.productActionPreOrder, "data-in-progress": isLoading }));
|
|
941
|
+
} }));
|
|
942
|
+
});
|
|
943
|
+
/**
|
|
944
|
+
* Actions namespace containing all product action components
|
|
945
|
+
* following the documented API: Product.Action.AddToCart, Product.Action.BuyNow, Product.Action.PreOrder
|
|
946
|
+
*/
|
|
947
|
+
export const Action = {
|
|
948
|
+
/** Add to cart action button */
|
|
949
|
+
AddToCart: ProductActionAddToCart,
|
|
950
|
+
/** Buy now action button */
|
|
951
|
+
BuyNow: ProductActionBuyNow,
|
|
952
|
+
/** Pre-order action button */
|
|
953
|
+
PreOrder: ProductActionPreOrder,
|
|
954
|
+
};
|
|
955
|
+
/**
|
|
956
|
+
* Helper function to determine stock status and label based on availability and can-pre-order settings
|
|
957
|
+
*/
|
|
958
|
+
function getStockStatusMessage(inStock, isPreOrderEnabled, availabilityStatus, labels) {
|
|
959
|
+
// Pre-order takes precedence
|
|
960
|
+
if (isPreOrderEnabled && !inStock) {
|
|
961
|
+
return {
|
|
962
|
+
status: 'can-pre-order',
|
|
963
|
+
label: labels.preOrder,
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
// Handle stock status based on availability
|
|
967
|
+
if (inStock) {
|
|
968
|
+
switch (availabilityStatus) {
|
|
969
|
+
case InventoryAvailabilityStatus.IN_STOCK:
|
|
970
|
+
return {
|
|
971
|
+
status: 'in-stock',
|
|
972
|
+
label: labels.inStock,
|
|
973
|
+
};
|
|
974
|
+
case InventoryAvailabilityStatus.PARTIALLY_OUT_OF_STOCK:
|
|
975
|
+
return {
|
|
976
|
+
status: 'limited-stock',
|
|
977
|
+
label: labels.limitedStock,
|
|
978
|
+
};
|
|
979
|
+
default:
|
|
980
|
+
return {
|
|
981
|
+
status: 'in-stock',
|
|
982
|
+
label: labels.inStock,
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
return {
|
|
987
|
+
status: 'out-of-stock',
|
|
988
|
+
label: labels.outOfStock,
|
|
989
|
+
};
|
|
990
|
+
}
|
|
991
|
+
/**
|
|
992
|
+
* Displays the selected variant stock status with customizable rendering and labels,
|
|
993
|
+
* including can-pre-order support. Similar to Product.Stock but for the selected variant.
|
|
994
|
+
*
|
|
995
|
+
* @component
|
|
996
|
+
* @example
|
|
997
|
+
* ```tsx
|
|
998
|
+
* // Default usage
|
|
999
|
+
* <Product.ProductVariant.Stock
|
|
1000
|
+
* className="stock-indicator"
|
|
1001
|
+
* labels={{
|
|
1002
|
+
* inStock: 'In Stock',
|
|
1003
|
+
* limitedStock: 'Limited Stock',
|
|
1004
|
+
* outOfStock: 'Out of Stock',
|
|
1005
|
+
* preOrder: 'Available for Pre-order'
|
|
1006
|
+
* }}
|
|
1007
|
+
* />
|
|
1008
|
+
*
|
|
1009
|
+
* // asChild with primitive
|
|
1010
|
+
* <Product.ProductVariant.Stock asChild>
|
|
1011
|
+
* <div className="variant-stock-status" />
|
|
1012
|
+
* </Product.ProductVariant.Stock>
|
|
1013
|
+
*
|
|
1014
|
+
* // asChild with react component
|
|
1015
|
+
* <Product.ProductVariant.Stock
|
|
1016
|
+
* labels={{
|
|
1017
|
+
* inStock: 'Available',
|
|
1018
|
+
* limitedStock: 'Low Stock',
|
|
1019
|
+
* outOfStock: 'Sold Out',
|
|
1020
|
+
* preOrder: 'Pre-order Now'
|
|
1021
|
+
* }}
|
|
1022
|
+
* asChild
|
|
1023
|
+
* >
|
|
1024
|
+
* {React.forwardRef(({status, label, ...props}, ref) => (
|
|
1025
|
+
* <div
|
|
1026
|
+
* ref={ref}
|
|
1027
|
+
* {...props}
|
|
1028
|
+
* className="flex items-center gap-2 data-[state='in-stock']:text-green-600 data-[state='limited-stock']:text-yellow-600 data-[state='out-of-stock']:text-red-600 data-[state='can-pre-order']:text-blue-600"
|
|
1029
|
+
* >
|
|
1030
|
+
* <div className="w-3 h-3 rounded-full data-[state='in-stock']:bg-green-500 data-[state='limited-stock']:bg-yellow-500 data-[state='out-of-stock']:bg-red-500 data-[state='can-pre-order']:bg-blue-500" />
|
|
1031
|
+
* <span className="text-sm font-medium">
|
|
1032
|
+
* {label}
|
|
1033
|
+
* </span>
|
|
1034
|
+
* </div>
|
|
1035
|
+
* ))}
|
|
1036
|
+
* </Product.ProductVariant.Stock>
|
|
1037
|
+
* ```
|
|
1038
|
+
*/
|
|
1039
|
+
export const ProductVariantStock = React.forwardRef((props, ref) => {
|
|
1040
|
+
const { asChild, children, className, labels } = props;
|
|
1041
|
+
return (_jsx(CoreProductVariantSelector.Stock, { children: ({ inStock, isPreOrderEnabled, availabilityStatus, currentVariantId, }) => {
|
|
1042
|
+
// Only render if we have a current variant selected
|
|
1043
|
+
if (!currentVariantId && !availabilityStatus) {
|
|
1044
|
+
return null;
|
|
1045
|
+
}
|
|
1046
|
+
// Default labels
|
|
1047
|
+
const defaultLabels = {
|
|
1048
|
+
inStock: 'In Stock',
|
|
1049
|
+
limitedStock: 'Limited Stock',
|
|
1050
|
+
outOfStock: 'Out of Stock',
|
|
1051
|
+
preOrder: 'Available for Pre-order',
|
|
1052
|
+
};
|
|
1053
|
+
const finalLabels = { ...defaultLabels, ...labels };
|
|
1054
|
+
// Get status and label using the helper function
|
|
1055
|
+
const { status, label } = getStockStatusMessage(inStock, isPreOrderEnabled, availabilityStatus, finalLabels);
|
|
1056
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productVariantStock, "data-state": status, customElement: children, customElementProps: {
|
|
1057
|
+
status,
|
|
1058
|
+
label,
|
|
1059
|
+
}, content: label, children: _jsx("span", { children: label }) }));
|
|
1060
|
+
} }));
|
|
1061
|
+
});
|
|
1062
|
+
/**
|
|
1063
|
+
* Displays the selected variant SKU with customizable rendering following the documented API.
|
|
1064
|
+
*
|
|
1065
|
+
* @component
|
|
1066
|
+
* @example
|
|
1067
|
+
* ```tsx
|
|
1068
|
+
* // Default usage
|
|
1069
|
+
* <Product.ProductVariant.SKU className="text-content-secondary" />
|
|
1070
|
+
*
|
|
1071
|
+
* // asChild with primitive
|
|
1072
|
+
* <Product.ProductVariant.SKU asChild>
|
|
1073
|
+
* <span className="sku-value" />
|
|
1074
|
+
* </Product.ProductVariant.SKU>
|
|
1075
|
+
*
|
|
1076
|
+
* // asChild with react component
|
|
1077
|
+
* <Product.ProductVariant.SKU asChild>
|
|
1078
|
+
* {React.forwardRef(({sku, ...props}, ref) => (
|
|
1079
|
+
* <span ref={ref} {...props}>
|
|
1080
|
+
* {sku}
|
|
1081
|
+
* </span>
|
|
1082
|
+
* ))}
|
|
1083
|
+
* </Product.ProductVariant.SKU>
|
|
1084
|
+
* ```
|
|
1085
|
+
*/
|
|
1086
|
+
export const ProductVariantSKU = React.forwardRef((props, ref) => {
|
|
1087
|
+
const { asChild, children, className } = props;
|
|
1088
|
+
return (_jsx(CoreSelectedVariant.Details, { children: ({ sku }) => {
|
|
1089
|
+
// Don't render anything if there's no SKU
|
|
1090
|
+
if (!sku) {
|
|
1091
|
+
return null;
|
|
1092
|
+
}
|
|
1093
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productVariantSku, customElement: children, customElementProps: { sku }, content: sku, children: _jsx("span", { children: sku }) }));
|
|
1094
|
+
} }));
|
|
1095
|
+
});
|
|
1096
|
+
/**
|
|
1097
|
+
* Displays the selected variant weight with customizable rendering following the documented API.
|
|
1098
|
+
*
|
|
1099
|
+
* @component
|
|
1100
|
+
* @example
|
|
1101
|
+
* ```tsx
|
|
1102
|
+
* // Default usage
|
|
1103
|
+
* <Product.ProductVariant.Weight className="text-content-secondary" />
|
|
1104
|
+
*
|
|
1105
|
+
* // asChild with primitive
|
|
1106
|
+
* <Product.ProductVariant.Weight asChild>
|
|
1107
|
+
* <span className="weight-value" />
|
|
1108
|
+
* </Product.ProductVariant.Weight>
|
|
1109
|
+
*
|
|
1110
|
+
* // asChild with react component
|
|
1111
|
+
* <Product.ProductVariant.Weight asChild>
|
|
1112
|
+
* {React.forwardRef(({weight, ...props}, ref) => (
|
|
1113
|
+
* <span ref={ref} {...props}>
|
|
1114
|
+
* {weight}
|
|
1115
|
+
* </span>
|
|
1116
|
+
* ))}
|
|
1117
|
+
* </Product.ProductVariant.Weight>
|
|
1118
|
+
* ```
|
|
1119
|
+
*/
|
|
1120
|
+
export const ProductVariantWeight = React.forwardRef((props, ref) => {
|
|
1121
|
+
const { asChild, children, className } = props;
|
|
1122
|
+
return (_jsx(CoreSelectedVariant.Details, { children: ({ weight }) => {
|
|
1123
|
+
// Don't render anything if there's no weight
|
|
1124
|
+
if (!weight) {
|
|
1125
|
+
return null;
|
|
1126
|
+
}
|
|
1127
|
+
return (_jsx(AsChildSlot, { ref: ref, asChild: asChild, className: className, "data-testid": TestIds.productVariantWeight, customElement: children, customElementProps: { weight }, content: weight, children: _jsx("span", { children: weight }) }));
|
|
1128
|
+
} }));
|
|
1129
|
+
});
|
|
1130
|
+
/**
|
|
1131
|
+
* ProductVariant namespace containing product variant components
|
|
1132
|
+
* following the compound component pattern: Product.ProductVariant.Stock, Product.ProductVariant.SKU, Product.ProductVariant.Weight
|
|
1133
|
+
*/
|
|
1134
|
+
export const ProductVariant = {
|
|
1135
|
+
/** Product variant stock component */
|
|
1136
|
+
Stock: ProductVariantStock,
|
|
1137
|
+
/** Product variant SKU component */
|
|
1138
|
+
SKU: ProductVariantSKU,
|
|
1139
|
+
/** Product variant weight component */
|
|
1140
|
+
Weight: ProductVariantWeight,
|
|
1141
|
+
};
|
|
1142
|
+
/**
|
|
1143
|
+
* Quantity namespace containing all product quantity components
|
|
1144
|
+
* following the compound component pattern: Product.Quantity.Root, Product.Quantity.Decrement, etc.
|
|
1145
|
+
*/
|
|
1146
|
+
export const Quantity = {
|
|
1147
|
+
/** Product quantity selector component */
|
|
1148
|
+
Root: ProductQuantity,
|
|
1149
|
+
/** Product quantity decrement component */
|
|
1150
|
+
Decrement: ProductQuantityDecrement,
|
|
1151
|
+
/** Product quantity input component */
|
|
1152
|
+
Input: ProductQuantityInput,
|
|
1153
|
+
/** Product quantity increment component */
|
|
1154
|
+
Increment: ProductQuantityIncrement,
|
|
1155
|
+
/** Product quantity raw component */
|
|
1156
|
+
Raw: ProductQuantityRaw,
|
|
1157
|
+
};
|