@shopbb/helium 0.6.3 → 0.7.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/dist/components/AddToCartButton.d.ts +17 -22
- package/dist/components/AddToCartButton.d.ts.map +1 -1
- package/dist/components/AddToCartButton.js +8 -45
- package/dist/components/AddToCartButton.js.map +1 -1
- package/dist/components/AddressForm.d.ts +42 -18
- package/dist/components/AddressForm.d.ts.map +1 -1
- package/dist/components/AddressForm.js +23 -20
- package/dist/components/AddressForm.js.map +1 -1
- package/dist/components/AddressList.d.ts +34 -17
- package/dist/components/AddressList.d.ts.map +1 -1
- package/dist/components/AddressList.js +7 -21
- package/dist/components/AddressList.js.map +1 -1
- package/dist/components/AddressPicker.d.ts +14 -16
- package/dist/components/AddressPicker.d.ts.map +1 -1
- package/dist/components/AddressPicker.js +10 -26
- package/dist/components/AddressPicker.js.map +1 -1
- package/dist/components/AnalyticsProvider.d.ts +5 -2
- package/dist/components/AnalyticsProvider.d.ts.map +1 -1
- package/dist/components/AnalyticsProvider.js +13 -11
- package/dist/components/AnalyticsProvider.js.map +1 -1
- package/dist/components/BuyNowButton.d.ts +7 -24
- package/dist/components/BuyNowButton.d.ts.map +1 -1
- package/dist/components/BuyNowButton.js +9 -43
- package/dist/components/BuyNowButton.js.map +1 -1
- package/dist/components/CartCheckoutButton.d.ts +10 -21
- package/dist/components/CartCheckoutButton.d.ts.map +1 -1
- package/dist/components/CartCheckoutButton.js +6 -11
- package/dist/components/CartCheckoutButton.js.map +1 -1
- package/dist/components/CartCost.d.ts +15 -23
- package/dist/components/CartCost.d.ts.map +1 -1
- package/dist/components/CartCost.js +1 -3
- package/dist/components/CartCost.js.map +1 -1
- package/dist/components/CartForm.d.ts +30 -102
- package/dist/components/CartForm.d.ts.map +1 -1
- package/dist/components/CartForm.js +32 -172
- package/dist/components/CartForm.js.map +1 -1
- package/dist/components/DiscountComponents.d.ts +67 -17
- package/dist/components/DiscountComponents.d.ts.map +1 -1
- package/dist/components/DiscountComponents.js +28 -74
- package/dist/components/DiscountComponents.js.map +1 -1
- package/dist/components/DiscountSelector.d.ts +50 -15
- package/dist/components/DiscountSelector.d.ts.map +1 -1
- package/dist/components/DiscountSelector.js +16 -44
- package/dist/components/DiscountSelector.js.map +1 -1
- package/dist/components/hooks/useOptimisticCart.d.ts +36 -37
- package/dist/components/hooks/useOptimisticCart.d.ts.map +1 -1
- package/dist/components/hooks/useOptimisticCart.js +95 -127
- package/dist/components/hooks/useOptimisticCart.js.map +1 -1
- package/dist/components/index.d.ts +24 -45
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +21 -37
- package/dist/components/index.js.map +1 -1
- package/dist/createCartHandler.d.ts.map +1 -1
- package/dist/createCartHandler.js +8 -1
- package/dist/createCartHandler.js.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -10
- package/src/components/AddToCartButton.tsx +34 -92
- package/src/components/AddressForm.tsx +56 -26
- package/src/components/AddressList.tsx +42 -33
- package/src/components/AddressPicker.tsx +19 -29
- package/src/components/AnalyticsProvider.tsx +18 -13
- package/src/components/BuyNowButton.tsx +28 -93
- package/src/components/CartCheckoutButton.tsx +16 -33
- package/src/components/CartCost.tsx +16 -28
- package/src/components/CartForm.tsx +87 -231
- package/src/components/DiscountComponents.tsx +94 -100
- package/src/components/DiscountSelector.tsx +68 -49
- package/src/components/hooks/useOptimisticCart.ts +122 -156
- package/src/components/index.ts +51 -99
- package/src/createCartHandler.ts +10 -1
- package/src/index.ts +0 -2
- /package/src/components/{AddressBookProvider.tsx → AddressBookProvider.tsx.deleted-0.7} +0 -0
- /package/src/components/{CartLineQuantityAdjustButton.tsx → CartLineQuantityAdjustButton.tsx.deleted-0.7} +0 -0
- /package/src/components/{CartProvider.tsx → CartProvider.tsx.deleted-0.7} +0 -0
- /package/src/components/{DiscountProvider.tsx → DiscountProvider.tsx.deleted-0.7} +0 -0
- /package/src/components/hooks/{useMounted.ts → useMounted.ts.deleted-0.7} +0 -0
- /package/src/{handleCartFormAction.ts → handleCartFormAction.ts.deleted-0.7} +0 -0
|
@@ -1,183 +1,149 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* useOptimisticCart —
|
|
2
|
+
* useOptimisticCart — 严格对齐 Shopify Hydrogen
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 的 cart 视图。让 UI 在网络回来之前立刻反应用户操作(数量+1 / 删除等)。
|
|
4
|
+
* 源文件参考:packages/hydrogen/src/cart/optimistic/useOptimisticCart.tsx
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* // cart 看起来已经"立刻应用了",但 fetcher 还在跑后端
|
|
6
|
+
* 核心:用 RR7 `useFetchers()` 拿当前所有 in-flight fetcher,遍历它们的 formData
|
|
7
|
+
* 用 CartForm.getFormInput 解析出 action/inputs,合并到 actualCart 上。
|
|
10
8
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
9
|
+
* 用法:
|
|
10
|
+
* const data = useLoaderData<typeof loader>();
|
|
11
|
+
* const cart = useOptimisticCart(data.cart);
|
|
12
|
+
* // cart 包含 isOptimistic 字段、cart.lines 包含 in-flight 添加的 line(也带 isOptimistic)
|
|
15
13
|
*
|
|
16
|
-
*
|
|
17
|
-
* -
|
|
18
|
-
* -
|
|
19
|
-
* - 不持久化(页面刷新清空)
|
|
14
|
+
* 与之前 helium 0.6.x 区别:
|
|
15
|
+
* - 删除自造 pendingStore 全局变量
|
|
16
|
+
* - 直接用 RR7 useFetchers()
|
|
20
17
|
*/
|
|
21
18
|
|
|
22
|
-
import
|
|
23
|
-
import {
|
|
19
|
+
import { useFetchers } from 'react-router';
|
|
20
|
+
import { CartForm, type CartLineInput } from '../CartForm';
|
|
24
21
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
// ============================================================
|
|
28
|
-
|
|
29
|
-
export interface PendingMutation {
|
|
30
|
-
id: string; // unique
|
|
31
|
-
action: 'LinesAdd' | 'LinesUpdate' | 'LinesRemove';
|
|
32
|
-
inputs: any;
|
|
22
|
+
interface LikeACart {
|
|
23
|
+
lines: { nodes: Array<any> };
|
|
33
24
|
}
|
|
34
25
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
add(m: PendingMutation) {
|
|
45
|
-
this.items.set(m.id, m);
|
|
46
|
-
this.cachedSnapshot = Array.from(this.items.values());
|
|
47
|
-
this.notify();
|
|
48
|
-
},
|
|
49
|
-
remove(id: string) {
|
|
50
|
-
if (this.items.delete(id)) {
|
|
51
|
-
this.cachedSnapshot = this.items.size === 0 ? EMPTY_SNAPSHOT : Array.from(this.items.values());
|
|
52
|
-
this.notify();
|
|
26
|
+
export type OptimisticCartLine<T = any> = T extends LikeACart
|
|
27
|
+
? T['lines']['nodes'][number] & { isOptimistic?: boolean }
|
|
28
|
+
: T & { isOptimistic?: boolean };
|
|
29
|
+
|
|
30
|
+
export type OptimisticCart<T = any> = T extends undefined | null
|
|
31
|
+
? {
|
|
32
|
+
isOptimistic?: boolean;
|
|
33
|
+
lines: { nodes: Array<OptimisticCartLine> };
|
|
34
|
+
totalQuantity?: number;
|
|
53
35
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
subscribe(fn: () => void): () => void {
|
|
60
|
-
this.listeners.add(fn);
|
|
61
|
-
return () => { this.listeners.delete(fn); };
|
|
62
|
-
},
|
|
63
|
-
notify() {
|
|
64
|
-
for (const fn of this.listeners) fn();
|
|
65
|
-
},
|
|
66
|
-
};
|
|
36
|
+
: Omit<T, 'lines'> & {
|
|
37
|
+
isOptimistic?: boolean;
|
|
38
|
+
lines: { nodes: Array<OptimisticCartLine<T>> };
|
|
39
|
+
totalQuantity?: number;
|
|
40
|
+
};
|
|
67
41
|
|
|
68
42
|
/**
|
|
69
|
-
*
|
|
43
|
+
* 给 optimistic line 一个临时 id。Hydrogen 用 `gid://shopify/CartLine/optimistic-<variantId>`。
|
|
44
|
+
* 我们 shopbb 也用类似前缀。
|
|
70
45
|
*/
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return () => pendingStore.remove(m.id);
|
|
46
|
+
function getOptimisticLineId(variantId: string): string {
|
|
47
|
+
return `optimistic-${variantId}`;
|
|
74
48
|
}
|
|
75
49
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
// ============================================================
|
|
79
|
-
|
|
80
|
-
export interface OptimisticCartLine {
|
|
81
|
-
id: string;
|
|
82
|
-
quantity: number;
|
|
83
|
-
isOptimistic?: boolean;
|
|
84
|
-
merchandise?: any;
|
|
85
|
-
cost?: any;
|
|
86
|
-
attributes?: any;
|
|
87
|
-
[key: string]: any;
|
|
50
|
+
function isOptimisticLineId(id: string): boolean {
|
|
51
|
+
return id.startsWith('optimistic-');
|
|
88
52
|
}
|
|
89
53
|
|
|
90
|
-
export
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
cost?: any;
|
|
95
|
-
[key: string]: any;
|
|
96
|
-
}
|
|
54
|
+
export function useOptimisticCart<DefaultCart = any>(
|
|
55
|
+
cart?: DefaultCart,
|
|
56
|
+
): OptimisticCart<DefaultCart> {
|
|
57
|
+
const fetchers = useFetchers();
|
|
97
58
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (!
|
|
112
|
-
// 如果没有 actual cart 但有 pending LinesAdd → 模拟一个空 cart + 即将添加的行
|
|
113
|
-
if (pendings.length === 0) return null;
|
|
114
|
-
const optimisticLines: OptimisticCartLine[] = [];
|
|
115
|
-
for (const p of pendings) {
|
|
116
|
-
if (p.action === 'LinesAdd' && Array.isArray(p.inputs?.lines)) {
|
|
117
|
-
for (const ln of p.inputs.lines) {
|
|
118
|
-
optimisticLines.push({
|
|
119
|
-
id: `__optimistic_${p.id}_${ln.merchandiseId}`,
|
|
120
|
-
quantity: ln.quantity ?? 1,
|
|
121
|
-
merchandise: { id: ln.merchandiseId },
|
|
122
|
-
isOptimistic: true,
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
if (optimisticLines.length === 0) return null;
|
|
128
|
-
return {
|
|
129
|
-
isOptimistic: true,
|
|
130
|
-
totalQuantity: optimisticLines.reduce((s, l) => s + l.quantity, 0),
|
|
131
|
-
lines: { nodes: optimisticLines },
|
|
132
|
-
} as TCart;
|
|
133
|
-
}
|
|
59
|
+
if (!fetchers || !fetchers.length) {
|
|
60
|
+
return cart as OptimisticCart<DefaultCart>;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 用 structuredClone 拷贝 cart(不污染 useLoaderData 的引用)
|
|
64
|
+
const optimisticCart: any = (cart as any)?.lines
|
|
65
|
+
? (typeof structuredClone === 'function' ? structuredClone(cart) : JSON.parse(JSON.stringify(cart)))
|
|
66
|
+
: { lines: { nodes: [] } };
|
|
67
|
+
|
|
68
|
+
const cartLines: any[] = optimisticCart.lines?.nodes ?? [];
|
|
69
|
+
let isOptimistic = false;
|
|
70
|
+
|
|
71
|
+
for (const { formData } of fetchers) {
|
|
72
|
+
if (!formData) continue;
|
|
134
73
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
74
|
+
let cartFormData: any;
|
|
75
|
+
try {
|
|
76
|
+
cartFormData = CartForm.getFormInput(formData);
|
|
77
|
+
} catch {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (!cartFormData?.action) continue;
|
|
81
|
+
|
|
82
|
+
if (cartFormData.action === CartForm.ACTIONS.LinesAdd) {
|
|
83
|
+
const lines: CartLineInput[] = cartFormData.inputs?.lines ?? [];
|
|
84
|
+
for (const input of lines) {
|
|
85
|
+
if (!input.selectedVariant) {
|
|
86
|
+
// 没传 selectedVariant,无法 optimistic 显示具体信息 — Hydrogen 的约束相同
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const variantId = (input.selectedVariant as any)?.id;
|
|
90
|
+
const existingLine = cartLines.find(
|
|
91
|
+
(line) => line.merchandise?.id === variantId,
|
|
92
|
+
);
|
|
93
|
+
isOptimistic = true;
|
|
94
|
+
if (existingLine) {
|
|
95
|
+
existingLine.quantity = (existingLine.quantity || 1) + (input.quantity || 1);
|
|
96
|
+
existingLine.isOptimistic = true;
|
|
97
|
+
} else {
|
|
98
|
+
cartLines.unshift({
|
|
99
|
+
id: getOptimisticLineId(variantId),
|
|
100
|
+
merchandise: input.selectedVariant,
|
|
101
|
+
quantity: input.quantity || 1,
|
|
102
|
+
isOptimistic: true,
|
|
103
|
+
});
|
|
159
104
|
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
105
|
+
}
|
|
106
|
+
} else if (cartFormData.action === CartForm.ACTIONS.LinesRemove) {
|
|
107
|
+
const lineIds: string[] = cartFormData.inputs?.lineIds ?? [];
|
|
108
|
+
for (const lineId of lineIds) {
|
|
109
|
+
const idx = cartLines.findIndex((line) => line.id === lineId);
|
|
110
|
+
if (idx !== -1) {
|
|
111
|
+
if (isOptimisticLineId(cartLines[idx].id)) continue;
|
|
112
|
+
cartLines.splice(idx, 1);
|
|
113
|
+
isOptimistic = true;
|
|
166
114
|
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
115
|
+
}
|
|
116
|
+
} else if (cartFormData.action === CartForm.ACTIONS.LinesUpdate) {
|
|
117
|
+
const lines: any[] = cartFormData.inputs?.lines ?? [];
|
|
118
|
+
for (const line of lines) {
|
|
119
|
+
const idx = cartLines.findIndex((optimisticLine) => line.id === optimisticLine.id);
|
|
120
|
+
if (idx > -1) {
|
|
121
|
+
if (isOptimisticLineId(cartLines[idx].id)) continue;
|
|
122
|
+
cartLines[idx].quantity = line.quantity as number;
|
|
123
|
+
if (cartLines[idx].quantity === 0) cartLines.splice(idx, 1);
|
|
124
|
+
isOptimistic = true;
|
|
171
125
|
}
|
|
172
126
|
}
|
|
173
127
|
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (isOptimistic) optimisticCart.isOptimistic = isOptimistic;
|
|
131
|
+
optimisticCart.totalQuantity = cartLines.reduce(
|
|
132
|
+
(sum, line) => sum + (line.quantity || 0),
|
|
133
|
+
0,
|
|
134
|
+
);
|
|
135
|
+
optimisticCart.lines = { ...optimisticCart.lines, nodes: cartLines };
|
|
136
|
+
|
|
137
|
+
return optimisticCart as OptimisticCart<DefaultCart>;
|
|
138
|
+
}
|
|
174
139
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
140
|
+
// 旧 API 兼容(pendingStore 已废弃)
|
|
141
|
+
export function __registerPendingMutation(): () => void {
|
|
142
|
+
return () => {};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface PendingMutation {
|
|
146
|
+
id: string;
|
|
147
|
+
action: 'LinesAdd' | 'LinesUpdate' | 'LinesRemove';
|
|
148
|
+
inputs: any;
|
|
183
149
|
}
|
package/src/components/index.ts
CHANGED
|
@@ -1,42 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @shopbb/helium/components — 商家用 React 组件
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Money, Image, ProductPrice,
|
|
9
|
-
* AddToCartButton, CartLineQuantityAdjustButton,
|
|
10
|
-
* VariantSelector, ProductOptionsProvider, useProductOptions,
|
|
11
|
-
* Analytics,
|
|
12
|
-
* } from '@shopbb/helium/components';
|
|
13
|
-
*
|
|
14
|
-
* 设计原则:
|
|
15
|
-
* - 无样式:组件只管行为 + 语义化 DOM,样式商家自己写
|
|
16
|
-
* - data-* 钩子:方便选择器
|
|
17
|
-
* - 对齐 Shopify Hydrogen 同名组件的 API,迁移成本低
|
|
18
|
-
* - Provider 链:<ShopProvider> > <CartProvider> > <AnalyticsProvider> > App
|
|
19
|
-
* 组件优先从 Provider 拿;Provider 缺省时回退到 props 或 noop
|
|
4
|
+
* 0.7 起严格对齐 Shopify Hydrogen 设计原则:
|
|
5
|
+
* - 数据流:服务端 loader → useLoaderData → 组件 props(不通过 Provider)
|
|
6
|
+
* - mutation:<CartForm> + RR7 useFetcher(自动 revalidate loader)
|
|
7
|
+
* - 删除 CartProvider / DiscountProvider / AddressBookProvider
|
|
20
8
|
*/
|
|
21
9
|
|
|
22
|
-
// Providers + hooks
|
|
10
|
+
// Providers + hooks(保留:仅 Shop / ProductOptions / CartLine / Analytics,对齐 Hydrogen)
|
|
23
11
|
export { ShopProvider, useShop, useShopOptional } from './ShopProvider';
|
|
24
12
|
export type { ShopContextValue, ShopProviderProps } from './ShopProvider';
|
|
25
|
-
// Hydrogen-compatible alias: <ShopifyProvider> 在我们叫 <ShopProvider>,提供别名让 Hydrogen 代码无痛迁移
|
|
26
13
|
export { ShopProvider as ShopifyProvider } from './ShopProvider';
|
|
27
14
|
export type { ShopProviderProps as ShopifyProviderProps } from './ShopProvider';
|
|
28
15
|
|
|
29
|
-
export { CartProvider, useCart, useCartOptional } from './CartProvider';
|
|
30
|
-
export type {
|
|
31
|
-
Cart,
|
|
32
|
-
CartLine,
|
|
33
|
-
CartLineMerchandise,
|
|
34
|
-
CartStatus,
|
|
35
|
-
CartUserError,
|
|
36
|
-
CartContextValue,
|
|
37
|
-
CartProviderProps,
|
|
38
|
-
} from './CartProvider';
|
|
39
|
-
|
|
40
16
|
export {
|
|
41
17
|
ProductOptionsProvider,
|
|
42
18
|
useProductOptions,
|
|
@@ -50,6 +26,10 @@ export type {
|
|
|
50
26
|
export { AnalyticsProvider, useAnalytics, Analytics } from './AnalyticsProvider';
|
|
51
27
|
export type { AnalyticsEvent, AnalyticsContextValue, AnalyticsProviderProps } from './AnalyticsProvider';
|
|
52
28
|
|
|
29
|
+
// CartLine context(Hydrogen 标准:cart line 子组件用 useCartLine 拿当前 line)
|
|
30
|
+
export { CartLineProvider, useCartLine, useCartLineOptional } from './CartLineProvider';
|
|
31
|
+
export type { CartLineProviderProps, CartLineLike } from './CartLineProvider';
|
|
32
|
+
|
|
53
33
|
// 展示型组件
|
|
54
34
|
export { Money } from './Money';
|
|
55
35
|
export type { MoneyProps, MoneyData, MoneyMeasurement } from './Money';
|
|
@@ -60,12 +40,6 @@ export type { ImageProps, ImageData } from './Image';
|
|
|
60
40
|
export { ProductPrice } from './ProductPrice';
|
|
61
41
|
export type { ProductPriceProps } from './ProductPrice';
|
|
62
42
|
|
|
63
|
-
export { AddToCartButton } from './AddToCartButton';
|
|
64
|
-
export type { AddToCartButtonProps } from './AddToCartButton';
|
|
65
|
-
|
|
66
|
-
export { CartLineQuantityAdjustButton } from './CartLineQuantityAdjustButton';
|
|
67
|
-
export type { CartLineQuantityAdjustButtonProps } from './CartLineQuantityAdjustButton';
|
|
68
|
-
|
|
69
43
|
export { VariantSelector } from './VariantSelector';
|
|
70
44
|
export type {
|
|
71
45
|
VariantSelectorProps,
|
|
@@ -73,51 +47,45 @@ export type {
|
|
|
73
47
|
VariantOption,
|
|
74
48
|
} from './VariantSelector';
|
|
75
49
|
|
|
76
|
-
|
|
77
|
-
export {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
50
|
+
export { CartLineQuantity } from './CartLineQuantity';
|
|
51
|
+
export type { CartLineQuantityProps } from './CartLineQuantity';
|
|
52
|
+
|
|
53
|
+
export { CartCost } from './CartCost';
|
|
54
|
+
export type { CartCostProps, MoneyValue } from './CartCost';
|
|
55
|
+
|
|
56
|
+
export { CartCheckoutButton } from './CartCheckoutButton';
|
|
57
|
+
export type { CartCheckoutButtonProps } from './CartCheckoutButton';
|
|
58
|
+
|
|
59
|
+
// CartForm + useFetcher 透传(Hydrogen 标准)
|
|
60
|
+
export { CartForm, CART_FORM_ACTIONS, useFetcher } from './CartForm';
|
|
82
61
|
export type {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
AddressBookUserError,
|
|
87
|
-
AddressBookContextValue,
|
|
88
|
-
AddressBookProviderProps,
|
|
89
|
-
} from './AddressBookProvider';
|
|
62
|
+
CartFormProps, CartFormInput, CartFormFetcher, CartFormAction,
|
|
63
|
+
CartLineInput, CartLineUpdateInput, FetcherWithComponents,
|
|
64
|
+
} from './CartForm';
|
|
90
65
|
|
|
91
|
-
|
|
92
|
-
export
|
|
66
|
+
// 按钮组件(内部用 CartForm)
|
|
67
|
+
export { AddToCartButton } from './AddToCartButton';
|
|
68
|
+
export type { AddToCartButtonProps } from './AddToCartButton';
|
|
69
|
+
export { BuyNowButton } from './BuyNowButton';
|
|
70
|
+
export type { BuyNowButtonProps } from './BuyNowButton';
|
|
93
71
|
|
|
94
|
-
|
|
95
|
-
export
|
|
72
|
+
// useOptimisticCart — 用 RR7 useFetchers() 实现(对齐 Hydrogen)
|
|
73
|
+
export { useOptimisticCart } from './hooks/useOptimisticCart';
|
|
74
|
+
export type { OptimisticCart, OptimisticCartLine, PendingMutation } from './hooks/useOptimisticCart';
|
|
96
75
|
|
|
76
|
+
// useMoney hook
|
|
77
|
+
export { useMoney } from './hooks/useMoney';
|
|
78
|
+
export type { MoneyV2, MoneyParts } from './hooks/useMoney';
|
|
79
|
+
|
|
80
|
+
// 地址簿(接 props,不再用 Provider)
|
|
81
|
+
export { AddressList } from './AddressList';
|
|
82
|
+
export type { AddressListProps, AddressListItemActions, Address } from './AddressList';
|
|
83
|
+
export { AddressForm } from './AddressForm';
|
|
84
|
+
export type { AddressFormProps, AddressFormI18n, AddressInput, AddressBookUserError, AddressFormSubmitResult } from './AddressForm';
|
|
97
85
|
export { AddressPicker } from './AddressPicker';
|
|
98
86
|
export type { AddressPickerProps } from './AddressPicker';
|
|
99
87
|
|
|
100
|
-
//
|
|
101
|
-
export {
|
|
102
|
-
DiscountProvider,
|
|
103
|
-
useDiscounts,
|
|
104
|
-
useDiscountsOptional,
|
|
105
|
-
useProductDiscounts,
|
|
106
|
-
} from './DiscountProvider';
|
|
107
|
-
export type {
|
|
108
|
-
Discount,
|
|
109
|
-
DiscountClaim,
|
|
110
|
-
DiscountValueType,
|
|
111
|
-
DiscountValuePercentage,
|
|
112
|
-
DiscountValueAmount,
|
|
113
|
-
DiscountValueFreeShipping,
|
|
114
|
-
CartDiscountCode,
|
|
115
|
-
CartDiscountAllocation,
|
|
116
|
-
DiscountUserError,
|
|
117
|
-
DiscountContextValue,
|
|
118
|
-
DiscountProviderProps,
|
|
119
|
-
} from './DiscountProvider';
|
|
120
|
-
|
|
88
|
+
// 优惠券(接 props,不再用 Provider)
|
|
121
89
|
export {
|
|
122
90
|
AppliedDiscountList,
|
|
123
91
|
ClaimableDiscountList,
|
|
@@ -129,36 +97,20 @@ export type {
|
|
|
129
97
|
ClaimableDiscountListProps,
|
|
130
98
|
DiscountClaimButtonProps,
|
|
131
99
|
MyDiscountListProps,
|
|
100
|
+
Discount,
|
|
101
|
+
DiscountClaim,
|
|
102
|
+
DiscountValueType,
|
|
103
|
+
DiscountValuePercentage,
|
|
104
|
+
DiscountValueAmount,
|
|
105
|
+
DiscountValueFreeShipping,
|
|
106
|
+
DiscountAllocation,
|
|
132
107
|
} from './DiscountComponents';
|
|
133
108
|
|
|
134
109
|
export { DiscountSelector } from './DiscountSelector';
|
|
135
|
-
export type { DiscountSelectorProps } from './DiscountSelector';
|
|
136
|
-
|
|
137
|
-
// SSR / hydrate 辅助 hook
|
|
138
|
-
export { useMounted } from './hooks/useMounted';
|
|
139
|
-
export { useOptimisticCart } from './hooks/useOptimisticCart';
|
|
140
|
-
export type { OptimisticCart, OptimisticCartLine, PendingMutation } from './hooks/useOptimisticCart';
|
|
141
|
-
export { useMoney } from './hooks/useMoney';
|
|
142
|
-
export type { MoneyV2, MoneyParts } from './hooks/useMoney';
|
|
143
|
-
|
|
144
|
-
// CartForm — Hydrogen-compatible form-based cart mutation API
|
|
145
|
-
export { CartForm, CART_FORM_ACTIONS, useFetcher } from './CartForm';
|
|
146
110
|
export type {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
} from './
|
|
150
|
-
|
|
151
|
-
// Cart line context + 子组件
|
|
152
|
-
export { CartLineProvider, useCartLine, useCartLineOptional } from './CartLineProvider';
|
|
153
|
-
export type { CartLineProviderProps, CartLineLike } from './CartLineProvider';
|
|
154
|
-
export { CartLineQuantity } from './CartLineQuantity';
|
|
155
|
-
export type { CartLineQuantityProps } from './CartLineQuantity';
|
|
156
|
-
export { CartCost } from './CartCost';
|
|
157
|
-
export type { CartCostProps, MoneyValue } from './CartCost';
|
|
158
|
-
export { CartCheckoutButton } from './CartCheckoutButton';
|
|
159
|
-
export type { CartCheckoutButtonProps } from './CartCheckoutButton';
|
|
160
|
-
export { BuyNowButton } from './BuyNowButton';
|
|
161
|
-
export type { BuyNowButtonProps } from './BuyNowButton';
|
|
111
|
+
DiscountSelectorProps,
|
|
112
|
+
AppliedDiscountClaim,
|
|
113
|
+
} from './DiscountSelector';
|
|
162
114
|
|
|
163
115
|
// Rich text
|
|
164
116
|
export { RichText } from './RichText';
|
package/src/createCartHandler.ts
CHANGED
|
@@ -56,10 +56,19 @@ export const DEFAULT_CART_FRAGMENT = /* GraphQL */ `
|
|
|
56
56
|
`;
|
|
57
57
|
|
|
58
58
|
export function createCartHandler(options: CartHandlerOptions): CartHandler {
|
|
59
|
-
const { storefront, getCartId, setCartId: rawSetId, responseHeaders } = options;
|
|
59
|
+
const { storefront, getCartId: rawGetCartId, setCartId: rawSetId, responseHeaders } = options;
|
|
60
|
+
|
|
61
|
+
// Stateful cart id: 在 createCart 后即时缓存,让 get() 拿到。
|
|
62
|
+
// 不然 RR7 action 后 loader revalidate 时 request.headers 没新 cookie,会丢 cart。
|
|
63
|
+
let currentCartId: string | null = rawGetCartId();
|
|
64
|
+
|
|
65
|
+
function getCartId(): string | null {
|
|
66
|
+
return currentCartId || rawGetCartId();
|
|
67
|
+
}
|
|
60
68
|
|
|
61
69
|
// Set the cart ID (delegate to options.setCartId, which writes Set-Cookie)
|
|
62
70
|
function setCartId(cartId: string): void {
|
|
71
|
+
currentCartId = cartId;
|
|
63
72
|
rawSetId(cartId, responseHeaders);
|
|
64
73
|
}
|
|
65
74
|
|
package/src/index.ts
CHANGED
|
@@ -18,8 +18,6 @@ export { createStorefrontClient } from './createStorefrontClient';
|
|
|
18
18
|
export { createCartHandler, DEFAULT_CART_FRAGMENT } from './createCartHandler';
|
|
19
19
|
export { cartGetIdDefault, cartSetIdDefault } from './cart-id';
|
|
20
20
|
export type { CartSetIdOptions } from './cart-id';
|
|
21
|
-
export { handleCartFormAction } from './handleCartFormAction';
|
|
22
|
-
export type { HandleCartFormActionOptions } from './handleCartFormAction';
|
|
23
21
|
export { CacheNone, CacheShort, CacheLong, CacheCustom } from './cache';
|
|
24
22
|
|
|
25
23
|
// 服务端工具
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|