-
Notifications
You must be signed in to change notification settings - Fork 428
Expand file tree
/
Copy pathProductForm.tsx
More file actions
150 lines (144 loc) · 4.7 KB
/
Copy pathProductForm.tsx
File metadata and controls
150 lines (144 loc) · 4.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import {Link, useNavigate} from 'react-router';
import {type MappedProductOptions} from '@shopify/hydrogen';
import type {
Maybe,
ProductOptionValueSwatch,
} from '@shopify/hydrogen/storefront-api-types';
import {AddToCartButton} from './AddToCartButton';
import {useAside} from './Aside';
import type {ProductFragment} from 'storefrontapi.generated';
export function ProductForm({
productOptions,
selectedVariant,
}: {
productOptions: MappedProductOptions[];
selectedVariant: ProductFragment['selectedOrFirstAvailableVariant'];
}) {
const navigate = useNavigate();
const {open} = useAside();
return (
<div className="product-form">
{productOptions.map((option) => {
// If there is only a single value in the option values, don't display the option
if (option.optionValues.length === 1) return null;
return (
<div className="product-options" key={option.name}>
<h5>{option.name}</h5>
<div className="product-options-grid">
{option.optionValues.map((value) => {
const {
name,
handle,
variantUriQuery,
selected,
available,
exists,
isDifferentProduct,
swatch,
} = value;
if (isDifferentProduct) {
// SEO
// When the variant is a combined listing child product
// that leads to a different url, we need to render it
// as an anchor tag
return (
<Link
className="product-options-item"
key={option.name + name}
prefetch="intent"
preventScrollReset
replace
to={`/products/${handle}?${variantUriQuery}`}
style={{
border: selected
? '1px solid black'
: '1px solid transparent',
opacity: available ? 1 : 0.3,
}}
>
<ProductOptionSwatch swatch={swatch} name={name} />
</Link>
);
} else {
// SEO
// When the variant is an update to the search param,
// render it as a button with javascript navigating to
// the variant so that SEO bots do not index these as
// duplicated links
return (
<button
type="button"
className={`product-options-item${
exists && !selected ? ' link' : ''
}`}
key={option.name + name}
style={{
border: selected
? '1px solid black'
: '1px solid transparent',
opacity: available ? 1 : 0.3,
}}
disabled={!exists}
onClick={() => {
if (!selected) {
void navigate(`?${variantUriQuery}`, {
replace: true,
preventScrollReset: true,
});
}
}}
>
<ProductOptionSwatch swatch={swatch} name={name} />
</button>
);
}
})}
</div>
<br />
</div>
);
})}
<AddToCartButton
disabled={!selectedVariant || !selectedVariant.availableForSale}
onClick={() => {
open('cart');
}}
lines={
selectedVariant
? [
{
merchandiseId: selectedVariant.id,
quantity: 1,
selectedVariant,
},
]
: []
}
>
{selectedVariant?.availableForSale ? 'Add to cart' : 'Sold out'}
</AddToCartButton>
</div>
);
}
function ProductOptionSwatch({
swatch,
name,
}: {
swatch?: Maybe<ProductOptionValueSwatch> | undefined;
name: string;
}) {
const image = swatch?.image?.previewImage?.url;
const color = swatch?.color;
if (!image && !color) return name;
return (
<div
aria-label={name}
className="product-option-label-swatch"
style={{
backgroundColor: color || 'transparent',
}}
>
{!!image && <img src={image} alt={name} />}
</div>
);
}