@rebuy/rebuy-hydrogen 3.0.0-beta.10 → 3.0.0-beta.2
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/dist/components/AddToCartBtn/AddToCartBtn.d.ts.map +1 -1
- package/dist/components/AddToCartBtn/HydrogenAddToCartBtn.d.ts.map +1 -1
- package/dist/components/AddToCartBtn/HydrogenReactAddToCartBtn.d.ts.map +1 -1
- package/dist/components/ProductCard/ProductCard.d.ts.map +1 -1
- package/dist/components/ProductPrice/ProductPrice.d.ts +1 -1
- package/dist/components/ProductPrice/ProductPrice.d.ts.map +1 -1
- package/dist/components/VariantSelect/VariantSelect.d.ts +1 -1
- package/dist/components/VariantSelect/VariantSelect.d.ts.map +1 -1
- package/dist/hooks/titleLevel.d.ts.map +1 -1
- package/dist/index.css +88 -88
- package/dist/index.css.map +4 -4
- package/dist/index.js +421 -195
- package/dist/index.js.map +4 -4
- package/dist/index.mjs +430 -196
- package/dist/index.mjs.map +4 -4
- package/dist/providers/RebuyHydrogenContextProvider.d.ts +1 -1
- package/dist/providers/RebuyHydrogenContextProvider.d.ts.map +1 -1
- package/dist/providers/RebuyHydrogenReactContextProvider.d.ts.map +1 -1
- package/dist/providers/types.d.ts +1 -7
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/types/rebuyCustom.d.ts +0 -1
- package/dist/types/rebuyCustom.d.ts.map +1 -1
- package/dist/types/shopify.d.ts.map +1 -1
- package/dist/types/widgets.d.ts +1 -1
- package/dist/types/widgets.d.ts.map +1 -1
- package/dist/utils/convertToRebuyProduct.d.ts.map +1 -1
- package/dist/utils/createContextParameters.d.ts.map +1 -1
- package/dist/utils/getEncodedAttributes.d.ts.map +1 -1
- package/dist/utils/getRebuyConfig.d.ts +1 -1
- package/dist/utils/getRebuyConfig.d.ts.map +1 -1
- package/dist/widgetContainer/RebuyWidgetContainer.d.ts +1 -1
- package/dist/widgetContainer/RebuyWidgetContainer.d.ts.map +1 -1
- package/dist/widgets/RebuyCompleteTheLook/RebuyCompleteTheLook.d.ts.map +1 -1
- package/dist/widgets/RebuyDynamicBundleProducts/BundleImages.d.ts.map +1 -1
- package/dist/widgets/RebuyDynamicBundleProducts/BundlePrice.d.ts.map +1 -1
- package/dist/widgets/RebuyDynamicBundleProducts/BundleSelection.d.ts +1 -1
- package/dist/widgets/RebuyDynamicBundleProducts/BundleSelection.d.ts.map +1 -1
- package/dist/widgets/RebuyDynamicBundleProducts/RebuyDynamicBundleProducts.d.ts.map +1 -1
- package/dist/widgets/RebuyDynamicBundleProducts/Select.d.ts.map +1 -1
- package/dist/widgets/RebuyProductAddOns/RebuyProductAddOnCard.d.ts.map +1 -1
- package/dist/widgets/RebuyProductAddOns/RebuyProductAddOns.d.ts.map +1 -1
- package/dist/widgets/RebuyProductAddOns/types.d.ts.map +1 -1
- package/dist/widgets/RebuyProductRecommendations/RebuyProductRecommendations.d.ts.map +1 -1
- package/package.json +36 -48
- package/src/components/AddToCartBtn/AddToCartBtn.tsx +45 -0
- package/src/components/AddToCartBtn/HydrogenAddToCartBtn.tsx +43 -0
- package/src/components/AddToCartBtn/HydrogenReactAddToCartBtn.tsx +35 -0
- package/src/components/AddToCartBtn/index.ts +1 -0
- package/src/components/AddToCartBtn/types.ts +27 -0
- package/src/components/ProductCard/ProductCard.tsx +70 -0
- package/src/components/ProductCard/index.ts +1 -0
- package/src/components/ProductCard/types.ts +10 -0
- package/src/components/ProductPrice/ProductPrice.tsx +49 -0
- package/src/components/ProductPrice/index.ts +1 -0
- package/src/components/Title/Title.tsx +19 -0
- package/src/components/Title/index.ts +1 -0
- package/src/components/Title/types.ts +7 -0
- package/src/components/VariantSelect/VariantSelect.tsx +45 -0
- package/src/components/VariantSelect/index.ts +1 -0
- package/src/components/VariantSelect/types.ts +6 -0
- package/src/context/RebuyContext.tsx +9 -0
- package/src/hooks/titleLevel.tsx +42 -0
- package/src/index.ts +7 -0
- package/src/providers/RebuyHydrogenContextProvider.tsx +112 -0
- package/src/providers/RebuyHydrogenReactContextProvider.tsx +192 -0
- package/src/providers/types.ts +58 -0
- package/src/queries/cart.queries.ts +467 -0
- package/src/types/common.ts +8 -0
- package/src/types/css.d.ts +11 -0
- package/src/types/env.d.ts +12 -0
- package/src/types/rebuy.d.ts +31 -0
- package/src/types/rebuyCustom.ts +263 -0
- package/src/types/rebuySmartCart.ts +188 -0
- package/src/types/shopify.ts +142 -0
- package/src/types/widgets.ts +29 -0
- package/src/utils/convertToRebuyProduct.tsx +319 -0
- package/src/utils/createContextParameters.ts +142 -0
- package/src/utils/getEncodedAttributes.ts +11 -0
- package/src/utils/getRebuyConfig.ts +31 -0
- package/src/widgetContainer/RebuyWidgetContainer.tsx +183 -0
- package/src/widgets/RebuyCompleteTheLook/RebuyCompleteTheLook.tsx +50 -0
- package/src/widgets/RebuyCompleteTheLook/index.ts +1 -0
- package/src/widgets/RebuyCompleteTheLook/types.ts +5 -0
- package/src/widgets/RebuyDynamicBundleProducts/BundleImages.tsx +62 -0
- package/src/widgets/RebuyDynamicBundleProducts/BundlePrice.tsx +93 -0
- package/src/widgets/RebuyDynamicBundleProducts/BundleSelection.tsx +65 -0
- package/src/widgets/RebuyDynamicBundleProducts/RebuyDynamicBundleProducts.tsx +118 -0
- package/src/widgets/RebuyDynamicBundleProducts/Select.tsx +41 -0
- package/src/widgets/RebuyDynamicBundleProducts/index.ts +1 -0
- package/src/widgets/RebuyDynamicBundleProducts/types.ts +23 -0
- package/src/widgets/RebuyProductAddOns/RebuyProductAddOnCard.tsx +66 -0
- package/src/widgets/RebuyProductAddOns/RebuyProductAddOns.tsx +218 -0
- package/src/widgets/RebuyProductAddOns/index.ts +1 -0
- package/src/widgets/RebuyProductAddOns/types.ts +24 -0
- package/src/widgets/RebuyProductRecommendations/RebuyProductRecommendations.tsx +50 -0
- package/src/widgets/RebuyProductRecommendations/index.ts +1 -0
- package/src/widgets/RebuyProductRecommendations/types.ts +5 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
import styles from '~/widgets/RebuyCompleteTheLook/RebuyCompleteTheLook.module.css';
|
2
|
+
|
3
|
+
import type { RebuyCompleteTheLookProps } from './types';
|
4
|
+
|
5
|
+
import { ProductCard } from '~/components/ProductCard';
|
6
|
+
import { Title } from '~/components/Title';
|
7
|
+
import { getTitleLevel } from '~/hooks/titleLevel';
|
8
|
+
|
9
|
+
// Component implementation
|
10
|
+
export const RebuyCompleteTheLook = (props: RebuyCompleteTheLookProps) => {
|
11
|
+
const {
|
12
|
+
addToCartBtnText = 'Add to cart',
|
13
|
+
addToCartCallback,
|
14
|
+
customTitle = `These pair with ${props.product?.title}`,
|
15
|
+
customTitleLevel = 'h2',
|
16
|
+
customTitleStyle,
|
17
|
+
products = [],
|
18
|
+
} = props;
|
19
|
+
|
20
|
+
if (products.length === 0) {
|
21
|
+
console.log('RebuyCompleteTheLook: No products found');
|
22
|
+
return null;
|
23
|
+
}
|
24
|
+
|
25
|
+
return (
|
26
|
+
<section className={styles.container}>
|
27
|
+
<Title
|
28
|
+
level={getTitleLevel(customTitleLevel)}
|
29
|
+
style={customTitleStyle}
|
30
|
+
text={customTitle}
|
31
|
+
/>
|
32
|
+
<ul className={styles.productGrid}>
|
33
|
+
{products.map((product) => (
|
34
|
+
<li className={styles.productItem} key={product.id}>
|
35
|
+
<ProductCard
|
36
|
+
addToCartBtnText={addToCartBtnText}
|
37
|
+
addToCartCallback={addToCartCallback}
|
38
|
+
isHydrogenReact={props.isHydrogenReact}
|
39
|
+
product={product}
|
40
|
+
productCardTitleLevel={getTitleLevel(
|
41
|
+
customTitleLevel,
|
42
|
+
true
|
43
|
+
)}
|
44
|
+
/>
|
45
|
+
</li>
|
46
|
+
))}
|
47
|
+
</ul>
|
48
|
+
</section>
|
49
|
+
);
|
50
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './RebuyCompleteTheLook';
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import { Link } from '@remix-run/react';
|
2
|
+
import { Image } from '@shopify/hydrogen';
|
3
|
+
import { Fragment } from 'react';
|
4
|
+
|
5
|
+
import styles from '~/widgets/RebuyDynamicBundleProducts/RebuyDynamicBundleProducts.module.css';
|
6
|
+
|
7
|
+
import type { BundleProduct } from '~/widgets/RebuyDynamicBundleProducts/types';
|
8
|
+
|
9
|
+
export const BundleImages = ({ products }: { products: BundleProduct[] }) => {
|
10
|
+
const selected = products.filter((product) => product.selected);
|
11
|
+
|
12
|
+
return (
|
13
|
+
<ul className={styles.bundleImages}>
|
14
|
+
{products.map((product, index) => {
|
15
|
+
const image = product.selectedVariant?.image;
|
16
|
+
const productImage = image ? (
|
17
|
+
<Image
|
18
|
+
alt={image.altText || `Picture of ${product.title}`}
|
19
|
+
className={styles.bundleImage}
|
20
|
+
data={image}
|
21
|
+
height={80}
|
22
|
+
title={product.title}
|
23
|
+
width={80}
|
24
|
+
/>
|
25
|
+
) : (
|
26
|
+
// No image defined
|
27
|
+
product.title
|
28
|
+
);
|
29
|
+
// Hide delimiter for first selected item
|
30
|
+
const showDelimiter = selected[0]?.id !== product.id;
|
31
|
+
|
32
|
+
return (
|
33
|
+
<Fragment key={product.id + '-BundleImages-' + index}>
|
34
|
+
{product.selected && (
|
35
|
+
<>
|
36
|
+
{showDelimiter && (
|
37
|
+
<li className={styles.bundleImageDelimiter}>
|
38
|
+
<span>+</span>
|
39
|
+
</li>
|
40
|
+
)}
|
41
|
+
<li className="flex items-center">
|
42
|
+
{product.default ? (
|
43
|
+
// Already on product page
|
44
|
+
productImage
|
45
|
+
) : (
|
46
|
+
// Link to product
|
47
|
+
<Link
|
48
|
+
title={product.title}
|
49
|
+
to={`/products/${product.handle}`}
|
50
|
+
>
|
51
|
+
{productImage}
|
52
|
+
</Link>
|
53
|
+
)}
|
54
|
+
</li>
|
55
|
+
</>
|
56
|
+
)}
|
57
|
+
</Fragment>
|
58
|
+
);
|
59
|
+
})}
|
60
|
+
</ul>
|
61
|
+
);
|
62
|
+
};
|
@@ -0,0 +1,93 @@
|
|
1
|
+
import { Money } from '@shopify/hydrogen';
|
2
|
+
|
3
|
+
import styles from './RebuyDynamicBundleProducts.module.css';
|
4
|
+
|
5
|
+
import type {
|
6
|
+
CurrencyCode,
|
7
|
+
MoneyV2,
|
8
|
+
} from '@shopify/hydrogen-react/storefront-api-types';
|
9
|
+
import type { BundleProduct } from '~/widgets/RebuyDynamicBundleProducts/types';
|
10
|
+
|
11
|
+
export const BundlePrice = ({ products }: { products: BundleProduct[] }) => {
|
12
|
+
const isDisabled =
|
13
|
+
products.filter((product) => product.selected).length < 1;
|
14
|
+
|
15
|
+
const totalBundlePrice = () => {
|
16
|
+
let total = 0;
|
17
|
+
let currencyCode = 'USD' as CurrencyCode;
|
18
|
+
|
19
|
+
for (const product of products) {
|
20
|
+
if (product.selected && product.selectedVariant) {
|
21
|
+
const { priceV2: price } = product.selectedVariant;
|
22
|
+
|
23
|
+
total += Number(price?.amount);
|
24
|
+
currencyCode = (price?.currencyCode || 'USD') as CurrencyCode;
|
25
|
+
}
|
26
|
+
}
|
27
|
+
|
28
|
+
return {
|
29
|
+
amount: String(total),
|
30
|
+
currencyCode,
|
31
|
+
};
|
32
|
+
};
|
33
|
+
|
34
|
+
const totalBundleCompareAtPrice = () => {
|
35
|
+
let compareAtTotal = 0;
|
36
|
+
let currencyCode = 'USD' as CurrencyCode;
|
37
|
+
|
38
|
+
for (const product of products) {
|
39
|
+
if (product.selected && product.selectedVariant) {
|
40
|
+
const { compareAtPriceV2: compareAtPrice, priceV2: price } =
|
41
|
+
product.selectedVariant;
|
42
|
+
|
43
|
+
currencyCode = (price?.currencyCode || 'USD') as CurrencyCode;
|
44
|
+
compareAtTotal += Number((compareAtPrice || price)?.amount);
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
return {
|
49
|
+
amount: String(compareAtTotal),
|
50
|
+
currencyCode,
|
51
|
+
};
|
52
|
+
};
|
53
|
+
const isDiscounted = (
|
54
|
+
price: { amount: string; currencyCode: string } | null,
|
55
|
+
compareAtPrice: { amount: string; currencyCode: string } | null
|
56
|
+
) => Number(compareAtPrice?.amount) > Number(price?.amount);
|
57
|
+
|
58
|
+
const price = totalBundlePrice();
|
59
|
+
const compareAtPrice = totalBundleCompareAtPrice();
|
60
|
+
|
61
|
+
const CompareAtPrice = ({
|
62
|
+
data: compareAtPrice,
|
63
|
+
}: {
|
64
|
+
data: MoneyV2 | null;
|
65
|
+
}) => {
|
66
|
+
return (
|
67
|
+
compareAtPrice && (
|
68
|
+
<Money
|
69
|
+
as="span"
|
70
|
+
className={styles.compareAtPrice}
|
71
|
+
data={compareAtPrice}
|
72
|
+
withoutTrailingZeros
|
73
|
+
/>
|
74
|
+
)
|
75
|
+
);
|
76
|
+
};
|
77
|
+
|
78
|
+
return (
|
79
|
+
products.length > 0 && (
|
80
|
+
<div className="flex items-center flex-col">
|
81
|
+
{!isDisabled && (
|
82
|
+
<p className="flex items-center gap-2 mb-2">
|
83
|
+
<span>Total Price:</span>
|
84
|
+
<Money as="span" data={price} withoutTrailingZeros />
|
85
|
+
{isDiscounted(price, compareAtPrice) && (
|
86
|
+
<CompareAtPrice data={compareAtPrice} />
|
87
|
+
)}
|
88
|
+
</p>
|
89
|
+
)}
|
90
|
+
</div>
|
91
|
+
)
|
92
|
+
);
|
93
|
+
};
|
@@ -0,0 +1,65 @@
|
|
1
|
+
import styles from '~/widgets/RebuyDynamicBundleProducts/RebuyDynamicBundleProducts.module.css';
|
2
|
+
|
3
|
+
import type { BundleSelectionProps } from '~/widgets/RebuyDynamicBundleProducts/types';
|
4
|
+
|
5
|
+
import { RebuyProductPrice } from '~/components/ProductPrice';
|
6
|
+
import { Select } from '~/widgets/RebuyDynamicBundleProducts/Select';
|
7
|
+
|
8
|
+
export const BundleSelection = ({
|
9
|
+
onSelectVariant,
|
10
|
+
onToggleBundleItem,
|
11
|
+
products,
|
12
|
+
}: BundleSelectionProps) => {
|
13
|
+
return (
|
14
|
+
<ul>
|
15
|
+
{products.map((product, index) => {
|
16
|
+
const { availableForSale } = product.selectedVariant;
|
17
|
+
const isOutOfStock = !availableForSale;
|
18
|
+
|
19
|
+
return (
|
20
|
+
<li
|
21
|
+
className={`${styles.bundleItemRowContainer} ${
|
22
|
+
!product.selected && styles.unselected
|
23
|
+
}`}
|
24
|
+
key={product.id + '-BundleSelection' + index}
|
25
|
+
>
|
26
|
+
<div className={styles.bundleItemRow}>
|
27
|
+
<input
|
28
|
+
checked={product.selected && availableForSale}
|
29
|
+
className="mt-1 rounded-sm accent-black cursor-pointer"
|
30
|
+
disabled={isOutOfStock}
|
31
|
+
id={`${product.id}-toggle`}
|
32
|
+
onChange={() => onToggleBundleItem(product)}
|
33
|
+
type="checkbox"
|
34
|
+
value={product.id}
|
35
|
+
/>
|
36
|
+
<div className={styles.bundleItemInput}>
|
37
|
+
<label
|
38
|
+
className={styles.bundleItemLabel}
|
39
|
+
htmlFor={`${product.id}-toggle`}
|
40
|
+
>
|
41
|
+
{isOutOfStock && <b>SOLD OUT</b>}
|
42
|
+
{product.default && <b>This item:</b>}
|
43
|
+
{product.title}
|
44
|
+
|
45
|
+
<RebuyProductPrice
|
46
|
+
selectedVariant={
|
47
|
+
product.selectedVariant
|
48
|
+
}
|
49
|
+
/>
|
50
|
+
</label>
|
51
|
+
|
52
|
+
{product.variants.nodes.length > 1 && (
|
53
|
+
<Select
|
54
|
+
onSelectVariant={onSelectVariant}
|
55
|
+
product={product}
|
56
|
+
/>
|
57
|
+
)}
|
58
|
+
</div>
|
59
|
+
</div>
|
60
|
+
</li>
|
61
|
+
);
|
62
|
+
})}
|
63
|
+
</ul>
|
64
|
+
);
|
65
|
+
};
|
@@ -0,0 +1,118 @@
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
2
|
+
|
3
|
+
import { BundlePrice } from './BundlePrice';
|
4
|
+
|
5
|
+
import styles from './RebuyDynamicBundleProducts.module.css';
|
6
|
+
|
7
|
+
import type {
|
8
|
+
BundleProduct,
|
9
|
+
RebuyDynamicBundleProductsProps,
|
10
|
+
} from '~/widgets/RebuyDynamicBundleProducts/types';
|
11
|
+
|
12
|
+
import { AddToCartBtn } from '~/components/AddToCartBtn';
|
13
|
+
import { Title } from '~/components/Title';
|
14
|
+
import { getTitleLevel } from '~/hooks/titleLevel';
|
15
|
+
import { convertToRebuyProduct } from '~/utils/convertToRebuyProduct';
|
16
|
+
import { BundleImages } from '~/widgets/RebuyDynamicBundleProducts/BundleImages';
|
17
|
+
import { BundleSelection } from '~/widgets/RebuyDynamicBundleProducts/BundleSelection';
|
18
|
+
|
19
|
+
export const RebuyDynamicBundleProducts = (
|
20
|
+
props: RebuyDynamicBundleProductsProps
|
21
|
+
) => {
|
22
|
+
const {
|
23
|
+
addToCartBtnText = 'Add to cart',
|
24
|
+
addToCartCallback,
|
25
|
+
customTitle = `These pair with ${props.product?.title}`,
|
26
|
+
customTitleLevel = 'h2',
|
27
|
+
customTitleStyle,
|
28
|
+
isHydrogenReact,
|
29
|
+
product,
|
30
|
+
products = [],
|
31
|
+
} = props;
|
32
|
+
const [bundleProducts, setBundleProducts] = useState<BundleProduct[]>([]);
|
33
|
+
|
34
|
+
// Initializiation
|
35
|
+
useEffect(() => {
|
36
|
+
const mainProduct: BundleProduct = convertToRebuyProduct(
|
37
|
+
isHydrogenReact || false,
|
38
|
+
product
|
39
|
+
);
|
40
|
+
|
41
|
+
const formattedProducts = products.map((product) => {
|
42
|
+
return {
|
43
|
+
...product,
|
44
|
+
selected: true,
|
45
|
+
selectedVariant: product.variants.nodes[0],
|
46
|
+
};
|
47
|
+
});
|
48
|
+
|
49
|
+
setBundleProducts([mainProduct, ...formattedProducts]);
|
50
|
+
}, [product, products, isHydrogenReact]);
|
51
|
+
|
52
|
+
const onToggleBundleItem = useCallback(
|
53
|
+
(product: BundleProduct) => {
|
54
|
+
product.selected = !product.selected;
|
55
|
+
|
56
|
+
setBundleProducts([...bundleProducts]);
|
57
|
+
},
|
58
|
+
[bundleProducts]
|
59
|
+
);
|
60
|
+
|
61
|
+
const onSelectVariant = useCallback(
|
62
|
+
(product: BundleProduct, variant_id: string) => {
|
63
|
+
const variant = product.variants.nodes.find(
|
64
|
+
({ id }) => id === variant_id
|
65
|
+
);
|
66
|
+
|
67
|
+
if (variant) {
|
68
|
+
product.selectedVariant = variant;
|
69
|
+
product.selected = variant.availableForSale || false;
|
70
|
+
|
71
|
+
setBundleProducts([...bundleProducts]);
|
72
|
+
}
|
73
|
+
},
|
74
|
+
[bundleProducts]
|
75
|
+
);
|
76
|
+
|
77
|
+
if (products.length === 0) {
|
78
|
+
console.log('RebuyDynamicBundleProducts: No products found');
|
79
|
+
return null;
|
80
|
+
}
|
81
|
+
|
82
|
+
return (
|
83
|
+
<section className={styles.container}>
|
84
|
+
<Title
|
85
|
+
level={getTitleLevel(customTitleLevel)}
|
86
|
+
style={customTitleStyle}
|
87
|
+
text={customTitle}
|
88
|
+
/>
|
89
|
+
<div className={styles.bundleContainer}>
|
90
|
+
<BundleImages products={bundleProducts} />
|
91
|
+
<BundlePrice products={bundleProducts} />
|
92
|
+
|
93
|
+
<div className={styles.addCartBtnContainer}>
|
94
|
+
<AddToCartBtn
|
95
|
+
addToCartBtnText={addToCartBtnText}
|
96
|
+
addToCartCallback={addToCartCallback}
|
97
|
+
disabled={
|
98
|
+
bundleProducts.filter((product) => product.selected)
|
99
|
+
.length === 0
|
100
|
+
}
|
101
|
+
isHydrogenReact={isHydrogenReact}
|
102
|
+
selectedVariants={bundleProducts
|
103
|
+
.filter((product) => product.selected)
|
104
|
+
.map((product) => {
|
105
|
+
return product.selectedVariant;
|
106
|
+
})}
|
107
|
+
/>
|
108
|
+
</div>
|
109
|
+
|
110
|
+
<BundleSelection
|
111
|
+
onSelectVariant={onSelectVariant}
|
112
|
+
onToggleBundleItem={onToggleBundleItem}
|
113
|
+
products={bundleProducts}
|
114
|
+
/>
|
115
|
+
</div>
|
116
|
+
</section>
|
117
|
+
);
|
118
|
+
};
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import styles from '~/widgets/RebuyDynamicBundleProducts/RebuyDynamicBundleProducts.module.css';
|
2
|
+
|
3
|
+
import type {
|
4
|
+
BundleProduct,
|
5
|
+
BundleSelectProps,
|
6
|
+
} from '~/widgets/RebuyDynamicBundleProducts/types';
|
7
|
+
|
8
|
+
export const Select = ({ onSelectVariant, product }: BundleSelectProps) => {
|
9
|
+
const getOptionsLabel = (product: BundleProduct) => {
|
10
|
+
const options = product.variants.nodes[0].selectedOptions;
|
11
|
+
const optionsFromKeys = options ? Object.keys(options[0]) : [];
|
12
|
+
const optionsFromValues = options
|
13
|
+
? options.map((option) => option.name)
|
14
|
+
: [];
|
15
|
+
const useValues = optionsFromKeys.every((key) =>
|
16
|
+
['name', 'value'].includes(key)
|
17
|
+
);
|
18
|
+
|
19
|
+
// Return delimited label for available option(s) e.g. Color / Size, Scent, etc
|
20
|
+
return (useValues ? optionsFromValues : optionsFromKeys).join(' / ');
|
21
|
+
};
|
22
|
+
|
23
|
+
return (
|
24
|
+
product && (
|
25
|
+
<select
|
26
|
+
aria-label="select variant"
|
27
|
+
className={styles.select}
|
28
|
+
onChange={(e) => onSelectVariant(product, e.target.value)}
|
29
|
+
value={product.selectedVariant.id}
|
30
|
+
>
|
31
|
+
<optgroup label={getOptionsLabel(product)}>
|
32
|
+
{product.variants.nodes.map(({ id, title }) => (
|
33
|
+
<option key={id + '-BundleVariant'} value={id}>
|
34
|
+
{title}
|
35
|
+
</option>
|
36
|
+
))}
|
37
|
+
</optgroup>
|
38
|
+
</select>
|
39
|
+
)
|
40
|
+
);
|
41
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './RebuyDynamicBundleProducts';
|
@@ -0,0 +1,23 @@
|
|
1
|
+
import type { RebuyProduct } from '~/types/rebuyCustom';
|
2
|
+
import type { WidgetChildProps } from '~/types/widgets';
|
3
|
+
|
4
|
+
export type BundleProductOptions = {
|
5
|
+
default?: boolean;
|
6
|
+
};
|
7
|
+
|
8
|
+
export type BundleProduct = RebuyProduct & BundleProductOptions;
|
9
|
+
|
10
|
+
export type BundleSelectionProps = {
|
11
|
+
onSelectVariant: (product: BundleProduct, variantId: string) => void;
|
12
|
+
onToggleBundleItem: (product: BundleProduct) => void;
|
13
|
+
products: BundleProduct[];
|
14
|
+
};
|
15
|
+
|
16
|
+
export type BundleSelectProps = {
|
17
|
+
onSelectVariant: (product: BundleProduct, variantId: string) => void;
|
18
|
+
product: BundleProduct;
|
19
|
+
};
|
20
|
+
|
21
|
+
export type RebuyDynamicBundleProductsProps = {
|
22
|
+
addToCartBtnText?: string;
|
23
|
+
} & WidgetChildProps;
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import { Link } from '@remix-run/react';
|
2
|
+
import { Image } from '@shopify/hydrogen';
|
3
|
+
|
4
|
+
import styles from './RebuyProductAddOns.module.css';
|
5
|
+
|
6
|
+
import type { RebuyProductAddOnCardProps } from './types';
|
7
|
+
|
8
|
+
import { RebuyProductPrice } from '~/components/ProductPrice';
|
9
|
+
import { Title } from '~/components/Title';
|
10
|
+
|
11
|
+
export const RebuyProductAddOnCard = ({
|
12
|
+
handleChange,
|
13
|
+
learnMoreText,
|
14
|
+
outOfStockText,
|
15
|
+
product,
|
16
|
+
titleLevel,
|
17
|
+
}: RebuyProductAddOnCardProps) => {
|
18
|
+
const { availableForSale, image } = product.selectedVariant || {};
|
19
|
+
|
20
|
+
return (
|
21
|
+
<label className={styles.addOnCard} htmlFor={product.id}>
|
22
|
+
<div className={styles.addOnCardContent}>
|
23
|
+
<input
|
24
|
+
checked={Boolean(product.selected)}
|
25
|
+
className={styles.addOnCardInput}
|
26
|
+
disabled={!product.variants.nodes[0].availableForSale}
|
27
|
+
id={product.id}
|
28
|
+
name={product.title}
|
29
|
+
onChange={(event) => handleChange(event, product)}
|
30
|
+
type="checkbox"
|
31
|
+
value=""
|
32
|
+
/>
|
33
|
+
<div className={styles.addOnCardContentImage}>
|
34
|
+
{image && (
|
35
|
+
<Image
|
36
|
+
alt={image.altText || `Picture of ${product.title}`}
|
37
|
+
data={image}
|
38
|
+
height={150}
|
39
|
+
width={300}
|
40
|
+
/>
|
41
|
+
)}
|
42
|
+
</div>
|
43
|
+
|
44
|
+
<div className={styles.addOnCardTextContent}>
|
45
|
+
{!availableForSale && (
|
46
|
+
<p className={styles.addOnCardOutOfStock}>
|
47
|
+
{outOfStockText}
|
48
|
+
</p>
|
49
|
+
)}
|
50
|
+
|
51
|
+
<Title level={titleLevel} text={product.title} />
|
52
|
+
|
53
|
+
<RebuyProductPrice
|
54
|
+
selectedVariant={product.selectedVariant}
|
55
|
+
/>
|
56
|
+
<Link
|
57
|
+
className={styles.addOnCardLearnMore}
|
58
|
+
to={`/products/${product.handle}`}
|
59
|
+
>
|
60
|
+
{learnMoreText}
|
61
|
+
</Link>
|
62
|
+
</div>
|
63
|
+
</div>
|
64
|
+
</label>
|
65
|
+
);
|
66
|
+
};
|