pb-sxp-ui 1.20.25 → 1.20.27

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.
Files changed (42) hide show
  1. package/dist/index.cjs +2164 -2248
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.css +355 -291
  4. package/dist/index.js +2164 -2248
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.min.cjs +6 -6
  7. package/dist/index.min.cjs.map +1 -1
  8. package/dist/index.min.js +6 -6
  9. package/dist/index.min.js.map +1 -1
  10. package/dist/pb-ui.js +2164 -2248
  11. package/dist/pb-ui.js.map +1 -1
  12. package/dist/pb-ui.min.js +6 -6
  13. package/dist/pb-ui.min.js.map +1 -1
  14. package/es/core/components/StructurePage/index.d.ts +4 -0
  15. package/es/core/components/StructurePage/index.js +14 -23
  16. package/es/core/components/SxpPageRender/Modal/index.d.ts +1 -0
  17. package/es/core/components/SxpPageRender/Modal/index.js +3 -3
  18. package/es/core/components/SxpPageRender/index.d.ts +1 -0
  19. package/es/core/components/SxpPageRender/typing.d.ts +1 -0
  20. package/es/materials/sxp/popup/AddToCart/index.d.ts +1 -27
  21. package/es/materials/sxp/popup/AddToCart/index.js +117 -173
  22. package/es/materials/sxp/popup/AddToCart/index.new.d.ts +8 -0
  23. package/es/materials/sxp/popup/AddToCart/index.new.js +174 -0
  24. package/es/materials/sxp/popup/AddToCart/index.old.d.ts +33 -0
  25. package/es/materials/sxp/popup/AddToCart/index.old.js +299 -0
  26. package/es/materials/sxp/popup/AddToCart/material.js +1 -54
  27. package/es/materials/sxp/popup/CommodityDetailDiroNew/index.js +48 -53
  28. package/lib/core/components/StructurePage/index.d.ts +4 -0
  29. package/lib/core/components/StructurePage/index.js +14 -23
  30. package/lib/core/components/SxpPageRender/Modal/index.d.ts +1 -0
  31. package/lib/core/components/SxpPageRender/Modal/index.js +3 -3
  32. package/lib/core/components/SxpPageRender/index.d.ts +1 -0
  33. package/lib/core/components/SxpPageRender/typing.d.ts +1 -0
  34. package/lib/materials/sxp/popup/AddToCart/index.d.ts +1 -27
  35. package/lib/materials/sxp/popup/AddToCart/index.js +115 -171
  36. package/lib/materials/sxp/popup/AddToCart/index.new.d.ts +8 -0
  37. package/lib/materials/sxp/popup/AddToCart/index.new.js +176 -0
  38. package/lib/materials/sxp/popup/AddToCart/index.old.d.ts +33 -0
  39. package/lib/materials/sxp/popup/AddToCart/index.old.js +301 -0
  40. package/lib/materials/sxp/popup/AddToCart/material.js +1 -54
  41. package/lib/materials/sxp/popup/CommodityDetailDiroNew/index.js +48 -53
  42. package/package.json +1 -1
@@ -3,53 +3,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const react_1 = tslib_1.__importStar(require("react"));
5
5
  const hooks_1 = require("../../../../core/hooks");
6
- const useEventReport_1 = require("../../../../core/hooks/useEventReport");
7
- const tool_1 = require("../../../../core/utils/tool");
6
+ const Modal_1 = tslib_1.__importDefault(require("../../../../core/components/SxpPageRender/Modal"));
8
7
  require("./index.less");
9
- const AddToCartPopup = (_a) => {
10
- var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
11
- var { style, isActive = true, index, shopifyDomain = '', storefrontAccessToken = '', variantStyles = {}, buttonStyle = {}, quantityStyle = {}, texts = {}, popupBg = {} } = _a, props = tslib_1.__rest(_a, ["style", "isActive", "index", "shopifyDomain", "storefrontAccessToken", "variantStyles", "buttonStyle", "quantityStyle", "texts", "popupBg"]);
12
- const { sxpParameter, popupDetailData, isPreview, bffFbReport, globalConfig } = (0, hooks_1.useSxpDataSource)();
13
- const { jumpToWeb } = (0, useEventReport_1.useEventReport)();
14
- const curTimeRef = (0, react_1.useRef)(null);
8
+ const AddToCartPopup = ({ isActive = true }) => {
9
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
10
+ const { popupDetailData, globalConfig } = (0, hooks_1.useSxpDataSource)();
15
11
  const [productData, setProductData] = (0, react_1.useState)(null);
16
12
  const [selectedOptions, setSelectedOptions] = (0, react_1.useState)({});
17
13
  const [selectedVariant, setSelectedVariant] = (0, react_1.useState)(null);
18
14
  const [quantity, setQuantity] = (0, react_1.useState)(1);
19
15
  const [loading, setLoading] = (0, react_1.useState)(true);
20
16
  const [error, setError] = (0, react_1.useState)(null);
17
+ const [showImagePreview, setShowImagePreview] = (0, react_1.useState)(false);
18
+ const [previewImageUrl, setPreviewImageUrl] = (0, react_1.useState)('');
21
19
  const data = popupDetailData;
22
- const product = (_c = (_b = data === null || data === void 0 ? void 0 : data.video) === null || _b === void 0 ? void 0 : _b.bindProduct) !== null && _c !== void 0 ? _c : (_e = (_d = data === null || data === void 0 ? void 0 : data.video) === null || _d === void 0 ? void 0 : _d.bindProducts) === null || _e === void 0 ? void 0 : _e[0];
23
- const cta = product === null || product === void 0 ? void 0 : product.bindCta;
24
- const position = (_g = (_f = popupDetailData === null || popupDetailData === void 0 ? void 0 : popupDetailData.index) !== null && _f !== void 0 ? _f : index) !== null && _g !== void 0 ? _g : 0;
25
- const finalShopifyDomain = shopifyDomain || (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.shopifyDomain) || 'dev-store-749237498237498636.myshopify.com';
26
- const finalStorefrontToken = storefrontAccessToken || (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.storefrontAccessToken) || '77d894c490f79430ce7bd0a7efdff6b7';
27
- const productId = (product === null || product === void 0 ? void 0 : product.itemId) || '';
28
- const finalTexts = {
29
- addToCart: texts.addToCart || 'Add to Cart',
30
- selectOptions: texts.selectOptions || 'Please select options',
31
- loading: texts.loading || 'Loading...',
32
- error: texts.error || 'Failed to load product',
33
- color: texts.color || 'Color',
34
- size: texts.size || 'Size',
35
- material: texts.material || 'Material',
36
- style: texts.style || 'Style'
37
- };
20
+ const product = (_e = (_b = (_a = data === null || data === void 0 ? void 0 : data.video) === null || _a === void 0 ? void 0 : _a.bindProduct) !== null && _b !== void 0 ? _b : (_d = (_c = data === null || data === void 0 ? void 0 : data.video) === null || _c === void 0 ? void 0 : _c.bindProducts) === null || _d === void 0 ? void 0 : _d[0]) !== null && _e !== void 0 ? _e : data === null || data === void 0 ? void 0 : data.product;
21
+ const shopifyConfig = window.__SHOPIFY_CONFIG__;
22
+ const shopifyDomain = ((_f = globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.shopify) === null || _f === void 0 ? void 0 : _f.domain) || (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.shopifyDomain) || (shopifyConfig === null || shopifyConfig === void 0 ? void 0 : shopifyConfig.domain) || 'dev-store-749237498237498636.myshopify.com';
23
+ const storefrontToken = ((_g = globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.shopify) === null || _g === void 0 ? void 0 : _g.storefrontAccessToken) || (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.storefrontAccessToken) || (shopifyConfig === null || shopifyConfig === void 0 ? void 0 : shopifyConfig.storefrontAccessToken) || '77d894c490f79430ce7bd0a7efdff6b7';
24
+ const productId = (product === null || product === void 0 ? void 0 : product.shopifyId) || (product === null || product === void 0 ? void 0 : product.itemId) || '';
38
25
  const fetchProductData = (0, react_1.useCallback)(() => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
39
- var _m;
40
- if (!productId || !finalShopifyDomain || !finalStorefrontToken) {
41
- console.log('[AddToCartPopup] 缺少必要配置:', {
42
- productId,
43
- shopifyDomain: finalShopifyDomain,
44
- hasToken: !!finalStorefrontToken
45
- });
26
+ var _k;
27
+ if (!productId || !shopifyDomain || !storefrontToken) {
46
28
  setLoading(false);
47
29
  return;
48
30
  }
49
- console.log('[AddToCartPopup] 开始加载商品数据:', {
50
- productId,
51
- shopifyDomain: finalShopifyDomain
52
- });
53
31
  setLoading(true);
54
32
  setError(null);
55
33
  try {
@@ -93,45 +71,34 @@ const AddToCartPopup = (_a) => {
93
71
  }
94
72
  }
95
73
  `;
96
- const formattedProductId = productId.startsWith('gid://')
97
- ? productId
98
- : `gid://shopify/Product/${productId}`;
99
- console.log('[AddToCartPopup] 使用的 Product ID:', formattedProductId);
100
- const response = yield fetch(`https://${finalShopifyDomain}/api/2024-01/graphql.json`, {
74
+ const fullProductId = productId.startsWith('gid://') ? productId : `gid://shopify/Product/${productId}`;
75
+ const response = yield fetch(`https://${shopifyDomain}/api/2024-01/graphql.json`, {
101
76
  method: 'POST',
102
77
  headers: {
103
78
  'Content-Type': 'application/json',
104
- 'X-Shopify-Storefront-Access-Token': finalStorefrontToken
79
+ 'X-Shopify-Storefront-Access-Token': storefrontToken
105
80
  },
106
81
  body: JSON.stringify({
107
82
  query,
108
- variables: { id: formattedProductId }
83
+ variables: { id: fullProductId }
109
84
  })
110
85
  });
111
- if (!response.ok) {
112
- throw new Error(`HTTP ${response.status}`);
113
- }
114
86
  const result = yield response.json();
115
87
  if (result.errors) {
116
- console.error('[AddToCartPopup] GraphQL 错误:', result.errors);
117
88
  throw new Error(result.errors[0].message);
118
89
  }
119
- if (!((_m = result.data) === null || _m === void 0 ? void 0 : _m.product)) {
120
- console.error('[AddToCartPopup] 未找到商品');
121
- throw new Error('Product not found');
122
- }
123
- console.log('[AddToCartPopup] 商品数据加载成功:', result.data.product.title);
90
+ console.log('[AddToCart] Shopify Product Data:', result.data.product);
91
+ console.log('[AddToCart] Options:', (_k = result.data.product) === null || _k === void 0 ? void 0 : _k.options);
124
92
  setProductData(result.data.product);
125
93
  }
126
94
  catch (err) {
127
- const errorMessage = err instanceof Error ? err.message : finalTexts.error;
128
- setError(errorMessage);
95
+ setError(err instanceof Error ? err.message : 'Failed to load product');
129
96
  console.error('[AddToCartPopup] 加载失败:', err);
130
97
  }
131
98
  finally {
132
99
  setLoading(false);
133
100
  }
134
- }), [productId, finalShopifyDomain, finalStorefrontToken, finalTexts.error]);
101
+ }), [productId, shopifyDomain, storefrontToken]);
135
102
  (0, react_1.useEffect)(() => {
136
103
  if (isActive) {
137
104
  fetchProductData();
@@ -148,134 +115,111 @@ const AddToCartPopup = (_a) => {
148
115
  return;
149
116
  }
150
117
  const matchedVariant = variants.find(variant => {
151
- return variant.selectedOptions.every(option => selectedOptions[option.name] === option.value);
118
+ return variant.selectedOptions.every(option => {
119
+ return selectedOptions[option.name] === option.value;
120
+ });
152
121
  });
153
122
  setSelectedVariant(matchedVariant || null);
154
- setQuantity(1);
123
+ if ((matchedVariant === null || matchedVariant === void 0 ? void 0 : matchedVariant.quantityAvailable) !== undefined && matchedVariant.quantityAvailable > 0) {
124
+ setQuantity(prev => Math.min(prev, matchedVariant.quantityAvailable));
125
+ }
155
126
  }, [selectedOptions, productData]);
156
- const handleOptionSelect = (0, react_1.useCallback)((optionName, value) => {
157
- setSelectedOptions(prev => {
158
- const newOptions = Object.assign({}, prev);
159
- if (newOptions[optionName] === value) {
160
- delete newOptions[optionName];
161
- }
162
- else {
163
- newOptions[optionName] = value;
164
- }
165
- return newOptions;
166
- });
167
- }, []);
168
- const handleQuantityChange = (0, react_1.useCallback)((delta) => {
169
- setQuantity(prev => {
170
- var _a;
171
- const newQuantity = prev + delta;
172
- const maxQuantity = (_a = selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.quantityAvailable) !== null && _a !== void 0 ? _a : 999;
173
- return Math.max(1, Math.min(newQuantity, maxQuantity));
174
- });
175
- }, [selectedVariant]);
176
- const isOptionValueAvailable = (0, react_1.useCallback)((optionName, value) => {
177
- if (!productData)
178
- return false;
179
- const variants = productData.variants.edges.map(edge => edge.node);
180
- const tempOptions = Object.assign(Object.assign({}, selectedOptions), { [optionName]: value });
181
- return variants.some(variant => {
182
- const matches = variant.selectedOptions.every(option => !tempOptions[option.name] || tempOptions[option.name] === option.value);
183
- const hasStock = variant.quantityAvailable === null || variant.quantityAvailable > 0;
184
- return matches && variant.availableForSale && hasStock;
185
- });
186
- }, [productData, selectedOptions]);
187
- const handleAddToCart = (0, react_1.useCallback)(() => {
188
- var _a;
189
- if (!selectedVariant || quantity === 0)
127
+ const handleOptionSelect = (optionName, value) => {
128
+ setSelectedOptions(prev => (Object.assign(Object.assign({}, prev), { [optionName]: value })));
129
+ };
130
+ const handleAddToCart = () => {
131
+ if (!selectedVariant) {
132
+ alert('Please select all options');
190
133
  return;
191
- const variantId = selectedVariant.id.split('/').pop();
192
- const cartUrl = `https://${finalShopifyDomain}/cart/add?id=${variantId}&quantity=${quantity}`;
193
- console.log('[AddToCartPopup] 加购:', {
134
+ }
135
+ const variantId = selectedVariant.id.replace('gid://shopify/ProductVariant/', '');
136
+ console.log('[AddToCart] 添加到购物车:', {
194
137
  variantId,
195
138
  quantity,
196
- cartUrl
139
+ shopifyDomain,
140
+ selectedVariant
197
141
  });
198
- bffFbReport === null || bffFbReport === void 0 ? void 0 : bffFbReport({
199
- eventName: 'AddToCart',
200
- product: product ? [product] : undefined,
201
- contentType: 'product',
202
- data,
203
- position,
204
- content_id: (_a = product === null || product === void 0 ? void 0 : product.itemId) !== null && _a !== void 0 ? _a : '',
205
- value: parseFloat(selectedVariant.price.amount) * quantity,
206
- currency: selectedVariant.price.currencyCode,
207
- contents: [{
208
- id: variantId,
209
- quantity
210
- }]
142
+ const params = new URLSearchParams({
143
+ id: variantId,
144
+ quantity: quantity.toString()
211
145
  });
146
+ const cartUrl = `https://${shopifyDomain}/cart/add?${params.toString()}`;
147
+ console.log('[AddToCart] 跳转到购物车 URL:', cartUrl);
212
148
  window.location.href = cartUrl;
213
- }, [selectedVariant, quantity, finalShopifyDomain, bffFbReport, product, data, position]);
214
- const totalPrice = (0, react_1.useMemo)(() => {
215
- if (!selectedVariant)
216
- return null;
217
- const price = parseFloat(selectedVariant.price.amount);
218
- const total = price * quantity;
219
- return total.toFixed(2);
220
- }, [selectedVariant, quantity]);
221
- (0, react_1.useEffect)(() => {
222
- const initTime = () => {
223
- curTimeRef.current = new Date();
224
- };
225
- initTime();
226
- window.addEventListener('pageshow', initTime);
227
- return () => {
228
- window.removeEventListener('pageshow', initTime);
229
- };
230
- }, []);
149
+ };
150
+ const totalPrice = selectedVariant
151
+ ? (parseFloat(selectedVariant.price.amount) * quantity).toFixed(2)
152
+ : '0.00';
231
153
  if (loading) {
232
- return (react_1.default.createElement("div", { className: "add-to-cart-popup-loading", style: style },
233
- react_1.default.createElement("div", null, finalTexts.loading)));
154
+ return (react_1.default.createElement("div", { className: 'add-to-cart-popup' },
155
+ react_1.default.createElement("div", { className: 'loading' }, "Loading...")));
156
+ }
157
+ if (error) {
158
+ return (react_1.default.createElement("div", { className: 'add-to-cart-popup' },
159
+ react_1.default.createElement("div", { className: 'error' }, error)));
234
160
  }
235
- if (error || !productData) {
236
- return (react_1.default.createElement("div", { className: "add-to-cart-popup-error", style: style },
237
- react_1.default.createElement("div", null,
238
- finalTexts.error,
239
- ": ",
240
- error || 'Product not found')));
161
+ if (!productData) {
162
+ return null;
241
163
  }
242
- const mainImage = ((_h = selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.image) === null || _h === void 0 ? void 0 : _h.url) || ((_j = productData.images.edges[0]) === null || _j === void 0 ? void 0 : _j.node.url) || ((_k = product === null || product === void 0 ? void 0 : product.homePage) === null || _k === void 0 ? void 0 : _k[0]) || '';
243
- const hasAllOptionsSelected = productData.options.length === Object.keys(selectedOptions).length;
244
- const isAddToCartDisabled = !selectedVariant || quantity === 0;
245
- return (react_1.default.createElement("div", Object.assign({ className: "add-to-cart-popup-container", style: style }, props),
246
- react_1.default.createElement("div", { className: "variant-detail-section" },
247
- react_1.default.createElement("div", { className: "variant-image-wrapper" },
248
- react_1.default.createElement("img", { src: mainImage, alt: productData.title, className: "variant-image" })),
249
- react_1.default.createElement("div", { className: "variant-info-wrapper" },
250
- react_1.default.createElement("h2", { className: "product-title-text", style: variantStyles.title, dangerouslySetInnerHTML: {
251
- __html: (0, tool_1.setFontForText)(productData.title, variantStyles.title)
252
- } }),
253
- selectedVariant && (react_1.default.createElement(react_1.default.Fragment, null,
254
- react_1.default.createElement("div", { className: "selected-options-tags" }, selectedVariant.selectedOptions.map(option => (react_1.default.createElement("span", { key: option.name, className: "option-tag", style: variantStyles.selectedOption },
255
- option.name,
256
- ": ",
257
- option.value)))),
258
- react_1.default.createElement("div", { className: "price-display" },
259
- react_1.default.createElement("span", { className: "price-value", style: variantStyles.price, dangerouslySetInnerHTML: {
260
- __html: (0, tool_1.setFontForText)(`${selectedVariant.price.currencyCode} $${totalPrice}`, variantStyles.price)
261
- } })),
262
- react_1.default.createElement("div", { className: "quantity-selector-wrapper", style: quantityStyle },
263
- react_1.default.createElement("button", { className: "quantity-btn quantity-decrease", onClick: () => handleQuantityChange(-1), disabled: quantity <= 1, "aria-label": "Decrease quantity" }, "-"),
264
- react_1.default.createElement("input", { type: "number", value: quantity, readOnly: true, className: "quantity-input-field", "aria-label": "Quantity" }),
265
- react_1.default.createElement("button", { className: "quantity-btn quantity-increase", onClick: () => handleQuantityChange(1), disabled: quantity >= ((_l = selectedVariant.quantityAvailable) !== null && _l !== void 0 ? _l : 999), "aria-label": "Increase quantity" }, "+")))),
266
- !hasAllOptionsSelected && (react_1.default.createElement("div", { className: "no-selection-hint", style: variantStyles.option }, finalTexts.selectOptions)))),
267
- react_1.default.createElement("div", { className: "variant-options-section" }, productData.options.map(option => (react_1.default.createElement("div", { key: option.name, className: "option-group-wrapper" },
268
- react_1.default.createElement("h3", { className: "option-group-name", style: variantStyles.option, dangerouslySetInnerHTML: {
269
- __html: (0, tool_1.setFontForText)(option.name, variantStyles.option)
270
- } }),
271
- react_1.default.createElement("div", { className: "option-values-grid" }, option.values.map(value => {
272
- const isSelected = selectedOptions[option.name] === value;
273
- const isAvailable = isOptionValueAvailable(option.name, value);
274
- return (react_1.default.createElement("button", { key: value, className: `option-value-button ${isSelected ? 'selected' : ''} ${!isAvailable ? 'disabled' : ''}`, onClick: () => isAvailable && handleOptionSelect(option.name, value), disabled: !isAvailable, "aria-label": `${option.name}: ${value}`, "aria-pressed": isSelected }, value));
275
- })))))),
276
- react_1.default.createElement("button", { className: `add-to-cart-button ${isAddToCartDisabled ? 'disabled' : ''}`, style: buttonStyle, onClick: handleAddToCart, disabled: isAddToCartDisabled, "aria-label": finalTexts.addToCart },
277
- react_1.default.createElement("span", { dangerouslySetInnerHTML: {
278
- __html: (0, tool_1.setFontForText)(finalTexts.addToCart, buttonStyle)
279
- } }))));
164
+ const mainImage = ((_h = productData.images.edges[0]) === null || _h === void 0 ? void 0 : _h.node.url) || '';
165
+ const variantImage = ((_j = selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.image) === null || _j === void 0 ? void 0 : _j.url) || mainImage;
166
+ const maxQuantity = (selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.quantityAvailable) || 999;
167
+ return (react_1.default.createElement("div", { className: 'add-to-cart-popup' },
168
+ react_1.default.createElement("div", { className: 'popup-content' },
169
+ react_1.default.createElement("div", { className: 'product-header' },
170
+ react_1.default.createElement("div", { className: 'product-title' }, productData.title)),
171
+ react_1.default.createElement("div", { className: 'variant-detail' },
172
+ react_1.default.createElement("div", { className: 'variant-image-container', onClick: () => {
173
+ if (variantImage) {
174
+ setPreviewImageUrl(variantImage);
175
+ setShowImagePreview(true);
176
+ }
177
+ }, style: { cursor: 'pointer' } },
178
+ react_1.default.createElement("img", { src: variantImage, alt: 'Selected variant', className: 'variant-image' })),
179
+ react_1.default.createElement("div", { className: 'variant-info' },
180
+ react_1.default.createElement("div", { className: 'variant-specs-row' },
181
+ react_1.default.createElement("div", { className: 'variant-specs' }, Object.keys(selectedOptions).length > 0 ? (Object.entries(selectedOptions).map(([key, value]) => (react_1.default.createElement("span", { key: key, className: 'spec-item' }, value)))) : (react_1.default.createElement("span", { className: 'spec-placeholder' }, "Please select options"))),
182
+ selectedVariant && selectedVariant.quantityAvailable !== undefined && (react_1.default.createElement("div", { className: 'stock-info' },
183
+ "Available: ",
184
+ selectedVariant.quantityAvailable))),
185
+ react_1.default.createElement("div", { className: 'variant-price-row' },
186
+ react_1.default.createElement("div", { className: 'price' },
187
+ "$",
188
+ totalPrice),
189
+ react_1.default.createElement("div", { className: 'quantity-selector' },
190
+ react_1.default.createElement("button", { className: 'qty-btn', onClick: () => setQuantity(Math.max(1, quantity - 1)), disabled: quantity <= 1 }, "\u2212"),
191
+ react_1.default.createElement("span", { className: 'qty-value' }, quantity),
192
+ react_1.default.createElement("button", { className: 'qty-btn', onClick: () => setQuantity(quantity + 1), disabled: !selectedVariant || quantity >= maxQuantity }, "+"))))),
193
+ react_1.default.createElement("div", { className: 'variant-options' }, productData.options.map(option => (react_1.default.createElement("div", { key: option.name, className: 'option-group' },
194
+ react_1.default.createElement("div", { className: 'option-label' }, option.name),
195
+ react_1.default.createElement("div", { className: 'option-values' }, option.values.map(value => {
196
+ const isAvailable = productData.variants.edges.some(({ node: variant }) => {
197
+ const hasThisOption = variant.selectedOptions.some(opt => opt.name === option.name && opt.value === value);
198
+ if (!hasThisOption || !variant.availableForSale)
199
+ return false;
200
+ if (variant.quantityAvailable !== undefined) {
201
+ return variant.quantityAvailable > 0;
202
+ }
203
+ return true;
204
+ });
205
+ const isSelected = selectedOptions[option.name] === value;
206
+ return (react_1.default.createElement("button", { key: value, className: `option-btn ${isSelected ? 'selected' : ''} ${!isAvailable ? 'disabled' : ''}`, onClick: () => isAvailable && handleOptionSelect(option.name, value), disabled: !isAvailable }, value));
207
+ }))))))),
208
+ react_1.default.createElement("div", { className: 'popup-footer' },
209
+ react_1.default.createElement("button", { className: 'add-to-cart-btn', onClick: handleAddToCart, disabled: !selectedVariant }, "Add To Cart")),
210
+ showImagePreview && previewImageUrl && (react_1.default.createElement(Modal_1.default, { visible: showImagePreview, padding: 0, isFullScreen: true, onClose: () => setShowImagePreview(false) },
211
+ react_1.default.createElement("div", { style: {
212
+ width: '100%',
213
+ height: '100%',
214
+ display: 'flex',
215
+ alignItems: 'center',
216
+ justifyContent: 'center',
217
+ backgroundColor: 'rgba(0, 0, 0, 0.9)'
218
+ }, onClick: () => setShowImagePreview(false) },
219
+ react_1.default.createElement("img", { src: previewImageUrl, alt: 'Preview', style: {
220
+ maxWidth: '100%',
221
+ maxHeight: '100%',
222
+ objectFit: 'contain'
223
+ } }))))));
280
224
  };
281
225
  exports.default = AddToCartPopup;
@@ -0,0 +1,8 @@
1
+ import { FC } from 'react';
2
+ import './index.less';
3
+ export interface IAddToCartPopupProps {
4
+ isActive?: boolean;
5
+ onClose?: () => void;
6
+ }
7
+ declare const AddToCartPopup: FC<IAddToCartPopupProps>;
8
+ export default AddToCartPopup;
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const react_1 = tslib_1.__importStar(require("react"));
5
+ const hooks_1 = require("../../../../core/hooks");
6
+ require("./index.less");
7
+ const AddToCartPopup = ({ isActive = true, onClose }) => {
8
+ var _a, _b, _c, _d, _e, _f, _g;
9
+ const { popupDetailData, globalConfig } = (0, hooks_1.useSxpDataSource)();
10
+ const [productData, setProductData] = (0, react_1.useState)(null);
11
+ const [selectedOptions, setSelectedOptions] = (0, react_1.useState)({});
12
+ const [selectedVariant, setSelectedVariant] = (0, react_1.useState)(null);
13
+ const [quantity, setQuantity] = (0, react_1.useState)(1);
14
+ const [loading, setLoading] = (0, react_1.useState)(true);
15
+ const [error, setError] = (0, react_1.useState)(null);
16
+ const data = popupDetailData;
17
+ const product = (_e = (_b = (_a = data === null || data === void 0 ? void 0 : data.video) === null || _a === void 0 ? void 0 : _a.bindProduct) !== null && _b !== void 0 ? _b : (_d = (_c = data === null || data === void 0 ? void 0 : data.video) === null || _c === void 0 ? void 0 : _c.bindProducts) === null || _d === void 0 ? void 0 : _d[0]) !== null && _e !== void 0 ? _e : data === null || data === void 0 ? void 0 : data.product;
18
+ const shopifyConfig = window.__SHOPIFY_CONFIG__;
19
+ const shopifyDomain = (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.shopifyDomain) || (shopifyConfig === null || shopifyConfig === void 0 ? void 0 : shopifyConfig.domain) || 'dev-store-749237498237498636.myshopify.com';
20
+ const storefrontToken = (globalConfig === null || globalConfig === void 0 ? void 0 : globalConfig.storefrontAccessToken) || (shopifyConfig === null || shopifyConfig === void 0 ? void 0 : shopifyConfig.storefrontAccessToken) || '77d894c490f79430ce7bd0a7efdff6b7';
21
+ const productId = (product === null || product === void 0 ? void 0 : product.shopifyId) || (product === null || product === void 0 ? void 0 : product.itemId) || '';
22
+ const fetchProductData = (0, react_1.useCallback)(() => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
23
+ if (!productId || !shopifyDomain || !storefrontToken) {
24
+ setLoading(false);
25
+ return;
26
+ }
27
+ setLoading(true);
28
+ setError(null);
29
+ try {
30
+ const query = `
31
+ query getProduct($id: ID!) {
32
+ product(id: $id) {
33
+ id
34
+ title
35
+ images(first: 10) {
36
+ edges {
37
+ node {
38
+ url
39
+ }
40
+ }
41
+ }
42
+ options {
43
+ name
44
+ values
45
+ }
46
+ variants(first: 100) {
47
+ edges {
48
+ node {
49
+ id
50
+ title
51
+ availableForSale
52
+ price {
53
+ amount
54
+ currencyCode
55
+ }
56
+ image {
57
+ url
58
+ }
59
+ selectedOptions {
60
+ name
61
+ value
62
+ }
63
+ }
64
+ }
65
+ }
66
+ }
67
+ }
68
+ `;
69
+ const fullProductId = productId.startsWith('gid://') ? productId : `gid://shopify/Product/${productId}`;
70
+ const response = yield fetch(`https://${shopifyDomain}/api/2024-01/graphql.json`, {
71
+ method: 'POST',
72
+ headers: {
73
+ 'Content-Type': 'application/json',
74
+ 'X-Shopify-Storefront-Access-Token': storefrontToken
75
+ },
76
+ body: JSON.stringify({
77
+ query,
78
+ variables: { id: fullProductId }
79
+ })
80
+ });
81
+ const result = yield response.json();
82
+ if (result.errors) {
83
+ throw new Error(result.errors[0].message);
84
+ }
85
+ setProductData(result.data.product);
86
+ }
87
+ catch (err) {
88
+ setError(err instanceof Error ? err.message : 'Failed to load product');
89
+ console.error('[AddToCartPopup] 加载失败:', err);
90
+ }
91
+ finally {
92
+ setLoading(false);
93
+ }
94
+ }), [productId, shopifyDomain, storefrontToken]);
95
+ (0, react_1.useEffect)(() => {
96
+ if (isActive) {
97
+ fetchProductData();
98
+ }
99
+ }, [isActive, fetchProductData]);
100
+ (0, react_1.useEffect)(() => {
101
+ if (!productData)
102
+ return;
103
+ const variants = productData.variants.edges.map(edge => edge.node);
104
+ const optionsCount = productData.options.length;
105
+ const selectedCount = Object.keys(selectedOptions).length;
106
+ if (selectedCount === 0 || selectedCount < optionsCount) {
107
+ setSelectedVariant(null);
108
+ return;
109
+ }
110
+ const matchedVariant = variants.find(variant => {
111
+ return variant.selectedOptions.every(option => {
112
+ return selectedOptions[option.name] === option.value;
113
+ });
114
+ });
115
+ setSelectedVariant(matchedVariant || null);
116
+ }, [selectedOptions, productData]);
117
+ const handleOptionSelect = (optionName, value) => {
118
+ setSelectedOptions(prev => (Object.assign(Object.assign({}, prev), { [optionName]: value })));
119
+ };
120
+ const handleAddToCart = () => {
121
+ if (!selectedVariant) {
122
+ alert('Please select all options');
123
+ return;
124
+ }
125
+ const variantId = selectedVariant.id.split('/').pop();
126
+ const checkoutUrl = `https://${shopifyDomain}/cart/${variantId}:${quantity}`;
127
+ window.location.href = checkoutUrl;
128
+ };
129
+ const totalPrice = selectedVariant
130
+ ? (parseFloat(selectedVariant.price.amount) * quantity).toFixed(2)
131
+ : '0.00';
132
+ const currency = (selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.price.currencyCode) || 'USD';
133
+ if (loading) {
134
+ return (react_1.default.createElement("div", { className: "add-to-cart-popup" },
135
+ react_1.default.createElement("div", { className: "loading" }, "Loading...")));
136
+ }
137
+ if (error) {
138
+ return (react_1.default.createElement("div", { className: "add-to-cart-popup" },
139
+ react_1.default.createElement("div", { className: "error" }, error)));
140
+ }
141
+ if (!productData) {
142
+ return null;
143
+ }
144
+ const mainImage = ((_f = productData.images.edges[0]) === null || _f === void 0 ? void 0 : _f.node.url) || '';
145
+ const variantImage = ((_g = selectedVariant === null || selectedVariant === void 0 ? void 0 : selectedVariant.image) === null || _g === void 0 ? void 0 : _g.url) || mainImage;
146
+ return (react_1.default.createElement("div", { className: "add-to-cart-popup" },
147
+ react_1.default.createElement("div", { className: "product-header" },
148
+ react_1.default.createElement("img", { src: mainImage, alt: productData.title, className: "product-thumb" }),
149
+ react_1.default.createElement("div", { className: "product-title" }, productData.title),
150
+ onClose && (react_1.default.createElement("button", { className: "close-btn", onClick: onClose }, "\u00D7"))),
151
+ react_1.default.createElement("div", { className: "variant-detail" },
152
+ react_1.default.createElement("div", { className: "variant-image-container" },
153
+ react_1.default.createElement("img", { src: variantImage, alt: "Selected variant", className: "variant-image" })),
154
+ react_1.default.createElement("div", { className: "variant-info" },
155
+ react_1.default.createElement("div", { className: "variant-specs" }, Object.keys(selectedOptions).length > 0 ? (Object.entries(selectedOptions).map(([key, value]) => (react_1.default.createElement("span", { key: key, className: "spec-item" }, value)))) : (react_1.default.createElement("span", { className: "spec-placeholder" }, "Please select options"))),
156
+ react_1.default.createElement("div", { className: "variant-price-row" },
157
+ react_1.default.createElement("div", { className: "price" },
158
+ "$",
159
+ totalPrice),
160
+ react_1.default.createElement("div", { className: "quantity-selector" },
161
+ react_1.default.createElement("button", { className: "qty-btn", onClick: () => setQuantity(Math.max(1, quantity - 1)), disabled: quantity <= 1 }, "\u2212"),
162
+ react_1.default.createElement("span", { className: "qty-value" }, quantity),
163
+ react_1.default.createElement("button", { className: "qty-btn", onClick: () => setQuantity(quantity + 1) }, "+"))))),
164
+ react_1.default.createElement("div", { className: "variant-options" }, productData.options.map(option => (react_1.default.createElement("div", { key: option.name, className: "option-group" },
165
+ react_1.default.createElement("div", { className: "option-label" }, option.name),
166
+ react_1.default.createElement("div", { className: "option-values" }, option.values.map(value => {
167
+ const isAvailable = productData.variants.edges.some(({ node: variant }) => {
168
+ const hasThisOption = variant.selectedOptions.some(opt => opt.name === option.name && opt.value === value);
169
+ return hasThisOption && variant.availableForSale;
170
+ });
171
+ const isSelected = selectedOptions[option.name] === value;
172
+ return (react_1.default.createElement("button", { key: value, className: `option-btn ${isSelected ? 'selected' : ''} ${!isAvailable ? 'disabled' : ''}`, onClick: () => isAvailable && handleOptionSelect(option.name, value), disabled: !isAvailable }, value));
173
+ })))))),
174
+ react_1.default.createElement("button", { className: "add-to-cart-btn", onClick: handleAddToCart, disabled: !selectedVariant }, "Add To Cart")));
175
+ };
176
+ exports.default = AddToCartPopup;
@@ -0,0 +1,33 @@
1
+ import { FC, CSSProperties } from 'react';
2
+ import './index.less';
3
+ export interface IAddToCartPopupProps {
4
+ style?: CSSProperties;
5
+ isActive?: boolean;
6
+ index?: number;
7
+ shopifyDomain?: string;
8
+ storefrontAccessToken?: string;
9
+ variantStyles?: {
10
+ title?: CSSProperties;
11
+ price?: CSSProperties;
12
+ option?: CSSProperties;
13
+ selectedOption?: CSSProperties;
14
+ };
15
+ buttonStyle?: CSSProperties;
16
+ quantityStyle?: CSSProperties;
17
+ texts?: {
18
+ addToCart?: string;
19
+ selectOptions?: string;
20
+ loading?: string;
21
+ error?: string;
22
+ color?: string;
23
+ size?: string;
24
+ material?: string;
25
+ style?: string;
26
+ };
27
+ popupBg?: {
28
+ horizontalMargin?: number;
29
+ bottomMargin?: number;
30
+ };
31
+ }
32
+ declare const AddToCartPopup: FC<IAddToCartPopupProps>;
33
+ export default AddToCartPopup;